1use crate::provider_access::{adopt, finish, read, retake};
3use crate::{
4 DbError, NewOperation, OperationId, PendingValue, ProviderAccess, ProviderAccessChange,
5 ProviderAccessRequest,
6};
7use coven_crypto::SecretBytes;
8
9impl crate::Database {
10 pub async fn provider_access(
12 &self,
13 account: String,
14 ) -> Result<Option<ProviderAccess>, DbError> {
15 self.call(move |inner| inner.access.with_writer(|db| read(db, &account))).await
16 }
17
18 pub async fn start_operation_with_access<P: PendingValue>(
20 &self,
21 operation: NewOperation,
22 changes: Vec<ProviderAccessChange>,
23 ) -> Result<OperationId, DbError> {
24 self.call(move |inner| {
25 inner.access.with_writer(|db| {
26 db.transaction(|db| {
27 let id = crate::operation::insert(db, &operation)?;
28 adopt::<P>(db, &changes)?;
29 Ok(id)
30 })
31 })
32 })
33 .await
34 }
35
36 pub async fn advance_operation_with_access<P: PendingValue>(
40 &self,
41 update: crate::OperationUpdate,
42 changes: Vec<ProviderAccessChange>,
43 child: Option<(OperationId, crate::OperationOutcome)>,
44 ) -> Result<(), DbError> {
45 self.call(move |inner| {
46 inner.access.with_writer(|db| {
47 db.transaction(|db| {
48 crate::operation::advance(db, &update)?;
49 if let Some((child, outcome)) = &child {
50 crate::operation::finish_with_outcome(db, *child, outcome)?;
51 }
52 adopt::<P>(db, &changes)
53 })
54 })
55 })
56 .await
57 }
58
59 pub async fn retake_provider_access<P: PendingValue>(
62 &self,
63 changes: Vec<ProviderAccessChange>,
64 ) -> Result<(), DbError> {
65 self.call(move |inner| {
66 inner.access.with_writer(|db| db.transaction(|db| retake::<P>(db, &changes)))
67 })
68 .await
69 }
70
71 pub async fn start_provider_request<P: PendingValue>(
74 &self,
75 account: String,
76 id: OperationId,
77 ) -> Result<bool, DbError> {
78 self.call(move |inner| {
79 inner.access.with_writer(|db| {
80 db.transaction(|db| {
81 let state = read(db, &account)?.ok_or(DbError::OperationChanged(id))?;
82 if state.request.as_ref().map(|r| r.operation) != Some(id) {
83 return Err(DbError::OperationChanged(id));
84 }
85 let record =
86 crate::operation::find(db, id)?.ok_or(DbError::OperationChanged(id))?;
87 if record.last_step == crate::provider_access::REQUEST_FIXED_STEP {
88 return Err(DbError::OperationChanged(id));
89 }
90 crate::provider_access::fix_target(db, &account)?;
91 crate::operation::set_step(db, id, crate::provider_access::REQUEST_FIXED_STEP)?;
92 crate::retry::clear(db, crate::RetrySubject::Operation(id))?;
93 crate::pending::save(db, &P::provider_access(id, account, state.intended))?;
94 Ok(state.intended)
95 })
96 })
97 })
98 .await
99 }
100
101 pub async fn fail_provider_request<P: PendingValue>(
108 &self,
109 account: String,
110 id: OperationId,
111 data: Vec<u8>,
112 pending: P,
113 ) -> Result<bool, DbError> {
114 let update = crate::OperationUpdate {
115 id,
116 previous: crate::provider_access::REQUEST_FIXED_STEP,
117 last_step: crate::provider_access::REQUEST_FAILED_STEP,
118 data,
119 };
120 self.call(move |inner| {
121 inner.access.with_writer(|db| {
122 db.transaction(|db| {
123 let state = read(db, &account)?.ok_or(DbError::OperationChanged(update.id))?;
124 let request = state.request.ok_or(DbError::OperationChanged(update.id))?;
125 if request.operation != update.id || pending.operation() != Some(update.id) {
126 return Err(DbError::Invariant {
127 what: "a provider request's condition names its own operation",
128 });
129 }
130 let target = request.target.ok_or(DbError::OperationChanged(update.id))?;
131 if state.intended != target {
132 crate::provider_access::requeue(db, &account, update.id)?;
133 crate::pending::save(
134 db,
135 &P::provider_access(update.id, account, state.intended),
136 )?;
137 return Ok(false);
138 }
139 crate::operation::advance(db, &update)?;
140 crate::pending::save(db, &pending)?;
141 Ok(true)
142 })
143 })
144 })
145 .await
146 }
147
148 pub async fn advance_operation_refusing_grant(
155 &self,
156 update: crate::OperationUpdate,
157 account: String,
158 grant: OperationId,
159 ) -> Result<(), DbError> {
160 self.call(move |inner| {
161 inner.access.with_writer(|db| {
162 db.transaction(|db| {
163 crate::operation::advance(db, &update)?;
164 let state = read(db, &account)?.ok_or(DbError::OperationChanged(grant))?;
165 if state.request
166 != Some(ProviderAccessRequest { operation: grant, target: Some(true) })
167 {
168 return Err(DbError::OperationChanged(grant));
169 }
170 crate::provider_access::confirm(db, &account, false, None)?;
171 finish(db, &account, grant)
172 })
173 })
174 })
175 .await
176 }
177
178 pub async fn complete_provider_request<P: PendingValue>(
181 &self,
182 account: String,
183 id: OperationId,
184 target: bool,
185 invitation: Option<SecretBytes>,
186 ) -> Result<bool, DbError> {
187 self.call(move |inner| {
188 inner.access.with_writer(|db| {
189 db.transaction(|db| {
190 let state = read(db, &account)?.ok_or(DbError::OperationChanged(id))?;
191 if state.request
192 != Some(ProviderAccessRequest { operation: id, target: Some(target) })
193 {
194 return Err(DbError::OperationChanged(id));
195 }
196 crate::provider_access::confirm(db, &account, target, invitation.as_ref())?;
197 if state.intended == target {
198 finish(db, &account, id)?;
199 Ok(true)
200 } else {
201 crate::provider_access::requeue(db, &account, id)?;
202 crate::pending::save(db, &P::provider_access(id, account, state.intended))?;
203 Ok(false)
204 }
205 })
206 })
207 })
208 .await
209 }
210}
211
212#[cfg(test)]
213#[path = "database_provider_access_tests.rs"]
214mod tests;