JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Implement `FromOperationState` for `AuthenticatedUser` and `AuthenticatedInstance`

Use `AuthenticatedUser` on repository requests so we can filter by privacy. Woohoo! Attempt to filter `UserRepositoriesRequest` responses by visibility to the requester.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨75dcac3

Showing ⁨⁨7⁩ changed files⁩ with ⁨⁨184⁩ insertions⁩ and ⁨⁨52⁩ deletions⁩

giterated-daemon/src/backend/git.rs

View file
@@ -16,8 +16,10 @@ use giterated_models::repository::{
16 16 use giterated_models::settings::{AnySetting, Setting};
17 17 use giterated_models::user::{User, UserParseError};
18 18 use giterated_models::value::{AnyValue, GiteratedObjectValue};
19 use giterated_stack::AuthenticatedUser;
19 20 use serde_json::Value;
20 21 use sqlx::PgPool;
22 use std::ops::Deref;
21 23 use std::{
22 24 path::{Path, PathBuf},
23 25 sync::Arc,
@@ -47,7 +49,7 @@ impl GitRepository {
47 49 pub async fn can_user_view_repository(
48 50 &self,
49 51 our_instance: &Instance,
50 user: Option<&User>,
52 user: &Option<AuthenticatedUser>,
51 53 settings: &Arc<Mutex<dyn MetadataBackend + Send>>,
52 54 ) -> bool {
53 55 info!(
@@ -64,7 +66,7 @@ impl GitRepository {
64 66 None => return false,
65 67 };
66 68
67 if *user == self.owner_user {
69 if *user.deref() == self.owner_user {
68 70 // owner can always view
69 71 return true;
70 72 }
@@ -99,7 +101,7 @@ impl GitRepository {
99 101 access_list
100 102 .0
101 103 .iter()
102 .find(|access_list_user| *access_list_user == user)
104 .find(|access_list_user| *access_list_user == user.deref())
103 105 .is_some()
104 106 } else {
105 107 false
@@ -241,7 +243,7 @@ impl GitBackend {
241 243 &self,
242 244 owner: &User,
243 245 name: &str,
244 requester: Option<&User>,
246 requester: &Option<AuthenticatedUser>,
245 247 ) -> Result<git2::Repository, GitBackendError> {
246 248 info!(
247 249 "Checking permissions for user {:?} on {}/{}",
@@ -261,7 +263,11 @@ impl GitBackend {
261 263
262 264 if let Some(requester) = requester {
263 265 if !repository
264 .can_user_view_repository(&self.instance, Some(requester), &self.settings_provider)
266 .can_user_view_repository(
267 &self.instance,
268 &Some(requester.clone()),
269 &self.settings_provider,
270 )
265 271 .await
266 272 {
267 273 return Err(GitBackendError::RepositoryNotFound {
@@ -329,12 +335,18 @@ impl GitBackend {
329 335
330 336 #[async_trait]
331 337 impl RepositoryBackend for GitBackend {
332 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> {
333 if let Ok(_repository) = self
338 async fn exists(
339 &mut self,
340 requester: &Option<AuthenticatedUser>,
341 repository: &Repository,
342 ) -> Result<bool, Error> {
343 if let Ok(repository) = self
334 344 .find_by_owner_user_name(&repository.owner.clone(), &repository.name)
335 345 .await
336 346 {
337 Ok(true)
347 Ok(repository
348 .can_user_view_repository(&self.instance, requester, &self.settings_provider)
349 .await)
338 350 } else {
339 351 Ok(false)
340 352 }
@@ -342,7 +354,7 @@ impl RepositoryBackend for GitBackend {
342 354
343 355 async fn create_repository(
344 356 &mut self,
345 _user: &User,
357 _user: &AuthenticatedUser,
346 358 request: &RepositoryCreateRequest,
347 359 ) -> Result<Repository, GitBackendError> {
348 360 // Check if repository already exists in the database
@@ -491,7 +503,7 @@ impl RepositoryBackend for GitBackend {
491 503
492 504 async fn repository_file_inspect(
493 505 &mut self,
494 requester: Option<&User>,
506 requester: &Option<AuthenticatedUser>,
495 507 repository: &Repository,
496 508 request: &RepositoryFileInspectRequest,
497 509 ) -> Result<Vec<RepositoryTreeEntry>, Error> {
@@ -616,7 +628,7 @@ impl RepositoryBackend for GitBackend {
616 628
617 629 async fn repository_file_from_id(
618 630 &mut self,
619 requester: Option<&User>,
631 requester: &Option<AuthenticatedUser>,
620 632 repository: &Repository,
621 633 request: &RepositoryFileFromIdRequest,
622 634 ) -> Result<RepositoryFile, Error> {
@@ -647,7 +659,7 @@ impl RepositoryBackend for GitBackend {
647 659
648 660 async fn repository_file_from_path(
649 661 &mut self,
650 requester: Option<&User>,
662 requester: &Option<AuthenticatedUser>,
651 663 repository: &Repository,
652 664 request: &RepositoryFileFromPathRequest,
653 665 ) -> Result<RepositoryFile, Error> {
@@ -733,7 +745,7 @@ impl RepositoryBackend for GitBackend {
733 745
734 746 async fn repository_diff(
735 747 &mut self,
736 requester: Option<&User>,
748 requester: &Option<AuthenticatedUser>,
737 749 repository: &Repository,
738 750 request: &RepositoryDiffRequest,
739 751 ) -> Result<RepositoryDiff, Error> {
@@ -862,7 +874,7 @@ impl RepositoryBackend for GitBackend {
862 874
863 875 async fn repository_diff_patch(
864 876 &mut self,
865 requester: Option<&User>,
877 requester: &Option<AuthenticatedUser>,
866 878 repository: &Repository,
867 879 request: &RepositoryDiffPatchRequest,
868 880 ) -> Result<String, Error> {
@@ -917,7 +929,7 @@ impl RepositoryBackend for GitBackend {
917 929
918 930 async fn repository_commit_before(
919 931 &mut self,
920 requester: Option<&User>,
932 requester: &Option<AuthenticatedUser>,
921 933 repository: &Repository,
922 934 request: &RepositoryCommitBeforeRequest,
923 935 ) -> Result<Commit, Error> {
@@ -967,7 +979,7 @@ impl RepositoryBackend for GitBackend {
967 979 impl IssuesBackend for GitBackend {
968 980 fn issues_count(
969 981 &mut self,
970 _requester: Option<&User>,
982 _requester: &Option<AuthenticatedUser>,
971 983 _request: &RepositoryIssuesCountRequest,
972 984 ) -> Result<u64, Error> {
973 985 todo!()
@@ -975,7 +987,7 @@ impl IssuesBackend for GitBackend {
975 987
976 988 fn issue_labels(
977 989 &mut self,
978 _requester: Option<&User>,
990 _requester: &Option<AuthenticatedUser>,
979 991 _request: &RepositoryIssueLabelsRequest,
980 992 ) -> Result<Vec<IssueLabel>, Error> {
981 993 todo!()
@@ -983,7 +995,7 @@ impl IssuesBackend for GitBackend {
983 995
984 996 fn issues(
985 997 &mut self,
986 _requester: Option<&User>,
998 _requester: &Option<AuthenticatedUser>,
987 999 _request: &RepositoryIssuesRequest,
988 1000 ) -> Result<Vec<RepositoryIssue>, Error> {
989 1001 todo!()

giterated-daemon/src/backend/mod.rs

View file
@@ -6,6 +6,7 @@ pub mod user;
6 6
7 7 use anyhow::Error;
8 8 use async_trait::async_trait;
9 use giterated_stack::AuthenticatedUser;
9 10 use serde_json::Value;
10 11
11 12 use crate::backend::git::GitBackendError;
@@ -30,42 +31,42 @@ use giterated_models::value::AnyValue;
30 31 pub trait RepositoryBackend {
31 32 async fn create_repository(
32 33 &mut self,
33 user: &User,
34 user: &AuthenticatedUser,
34 35 request: &RepositoryCreateRequest,
35 36 ) -> Result<Repository, GitBackendError>;
36 37 async fn repository_file_inspect(
37 38 &mut self,
38 requester: Option<&User>,
39 requester: &Option<AuthenticatedUser>,
39 40 repository: &Repository,
40 41 request: &RepositoryFileInspectRequest,
41 42 ) -> Result<Vec<RepositoryTreeEntry>, Error>;
42 43 async fn repository_file_from_id(
43 44 &mut self,
44 requester: Option<&User>,
45 requester: &Option<AuthenticatedUser>,
45 46 repository: &Repository,
46 47 request: &RepositoryFileFromIdRequest,
47 48 ) -> Result<RepositoryFile, Error>;
48 49 async fn repository_file_from_path(
49 50 &mut self,
50 requester: Option<&User>,
51 requester: &Option<AuthenticatedUser>,
51 52 repository: &Repository,
52 53 request: &RepositoryFileFromPathRequest,
53 54 ) -> Result<RepositoryFile, Error>;
54 55 async fn repository_diff(
55 56 &mut self,
56 requester: Option<&User>,
57 requester: &Option<AuthenticatedUser>,
57 58 repository: &Repository,
58 59 request: &RepositoryDiffRequest,
59 60 ) -> Result<RepositoryDiff, Error>;
60 61 async fn repository_diff_patch(
61 62 &mut self,
62 requester: Option<&User>,
63 requester: &Option<AuthenticatedUser>,
63 64 repository: &Repository,
64 65 request: &RepositoryDiffPatchRequest,
65 66 ) -> Result<String, Error>;
66 67 async fn repository_commit_before(
67 68 &mut self,
68 requester: Option<&User>,
69 requester: &Option<AuthenticatedUser>,
69 70 repository: &Repository,
70 71 request: &RepositoryCommitBeforeRequest,
71 72 ) -> Result<Commit, Error>;
@@ -81,23 +82,27 @@ pub trait RepositoryBackend {
81 82 name: &str,
82 83 setting: &Value,
83 84 ) -> Result<(), Error>;
84 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error>;
85 async fn exists(
86 &mut self,
87 requester: &Option<AuthenticatedUser>,
88 repository: &Repository,
89 ) -> Result<bool, Error>;
85 90 }
86 91
87 92 pub trait IssuesBackend {
88 93 fn issues_count(
89 94 &mut self,
90 requester: Option<&User>,
95 requester: &Option<AuthenticatedUser>,
91 96 request: &RepositoryIssuesCountRequest,
92 97 ) -> Result<u64, Error>;
93 98 fn issue_labels(
94 99 &mut self,
95 requester: Option<&User>,
100 requester: &Option<AuthenticatedUser>,
96 101 request: &RepositoryIssueLabelsRequest,
97 102 ) -> Result<Vec<IssueLabel>, Error>;
98 103 fn issues(
99 104 &mut self,
100 requester: Option<&User>,
105 requester: &Option<AuthenticatedUser>,
101 106 request: &RepositoryIssuesRequest,
102 107 ) -> Result<Vec<RepositoryIssue>, Error>;
103 108 }
@@ -129,7 +134,7 @@ pub trait UserBackend: AuthBackend {
129 134 async fn exists(&mut self, user: &User) -> Result<bool, Error>;
130 135 async fn repositories_for_user(
131 136 &mut self,
132 requester: Option<&User>,
137 requester: &Option<AuthenticatedUser>,
133 138 user: &User,
134 139 ) -> Result<Vec<RepositorySummary>, Error>;
135 140 }

giterated-daemon/src/backend/user.rs

View file
@@ -8,6 +8,7 @@ use giterated_models::repository::{Repository, RepositorySummary};
8 8 use giterated_models::settings::{AnySetting, Setting};
9 9 use giterated_models::user::{Bio, DisplayName, User, UserParseError};
10 10 use giterated_models::value::AnyValue;
11 use giterated_stack::AuthenticatedUser;
11 12 use std::sync::Arc;
12 13
13 14 use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, Key, KeyInit};
@@ -98,7 +99,7 @@ impl UserBackend for UserAuth {
98 99
99 100 async fn repositories_for_user(
100 101 &mut self,
101 _requester: Option<&User>,
102 _requester: &Option<AuthenticatedUser>,
102 103 user: &User,
103 104 ) -> Result<Vec<RepositorySummary>, Error> {
104 105 let mut repositories = sqlx::query_as!(

giterated-daemon/src/database_backend/handler.rs

View file
@@ -19,7 +19,7 @@ use giterated_models::{
19 19 user::{User, UserRepositoriesRequest},
20 20 value::{AnyValue, GetValue},
21 21 };
22 use giterated_stack::{BackendWrapper, StackOperationState};
22 use giterated_stack::{AuthenticatedUser, BackendWrapper, StackOperationState};
23 23
24 24 use super::DatabaseBackend;
25 25
@@ -27,16 +27,31 @@ pub fn user_get_repositories(
27 27 object: &User,
28 28 _operation: UserRepositoriesRequest,
29 29 state: DatabaseBackend,
30 operation_state: StackOperationState,
31 requester: Option<AuthenticatedUser>,
30 32 ) -> BoxFuture<'static, Result<Vec<RepositorySummary>, OperationError<UserError>>> {
31 33 let object = object.clone();
32 34
33 35 async move {
34 36 let mut user_backend = state.user_backend.lock().await;
35 let repositories = user_backend
36 .repositories_for_user(None, &object)
37 let repositories_response = user_backend
38 .repositories_for_user(&requester, &object)
37 39 .await
38 40 .map_err(|e| OperationError::Internal(e.to_string()))?;
39 41
42 let mut repositories = vec![];
43
44 for repository in repositories_response {
45 if operation_state
46 .giterated_backend
47 .get_object::<Repository>(&repository.repository.to_string(), &operation_state)
48 .await
49 .is_ok()
50 {
51 repositories.push(repository);
52 }
53 }
54
40 55 Ok(repositories)
41 56 }
42 57 .boxed()
@@ -105,6 +120,7 @@ pub fn repository_info(
105 120 state: DatabaseBackend,
106 121 operation_state: StackOperationState,
107 122 backend: BackendWrapper,
123 requester: Option<AuthenticatedUser>,
108 124 ) -> BoxFuture<'static, Result<RepositoryView, OperationError<RepositoryError>>> {
109 125 let object = object.clone();
110 126
@@ -117,7 +133,7 @@ pub fn repository_info(
117 133 let mut repository_backend = state.repository_backend.lock().await;
118 134 let tree = repository_backend
119 135 .repository_file_inspect(
120 None,
136 &requester,
121 137 object.object(),
122 138 &RepositoryFileInspectRequest {
123 139 extra_metadata: operation.extra_metadata,
@@ -158,6 +174,7 @@ pub fn repository_file_from_id(
158 174 state: DatabaseBackend,
159 175 operation_state: StackOperationState,
160 176 backend: BackendWrapper,
177 requester: Option<AuthenticatedUser>,
161 178 ) -> BoxFuture<'static, Result<RepositoryFile, OperationError<RepositoryError>>> {
162 179 let object = object.clone();
163 180
@@ -170,7 +187,7 @@ pub fn repository_file_from_id(
170 187 let mut repository_backend = state.repository_backend.lock().await;
171 188 let file = repository_backend
172 189 .repository_file_from_id(
173 None,
190 &requester,
174 191 object.object(),
175 192 &RepositoryFileFromIdRequest(operation.0),
176 193 )
@@ -189,6 +206,7 @@ pub fn repository_file_from_path(
189 206 state: DatabaseBackend,
190 207 operation_state: StackOperationState,
191 208 backend: BackendWrapper,
209 requester: Option<AuthenticatedUser>,
192 210 ) -> BoxFuture<'static, Result<RepositoryFile, OperationError<RepositoryError>>> {
193 211 let object = object.clone();
194 212
@@ -201,7 +219,7 @@ pub fn repository_file_from_path(
201 219 let mut repository_backend = state.repository_backend.lock().await;
202 220 let file = repository_backend
203 221 .repository_file_from_path(
204 None,
222 &requester,
205 223 object.object(),
206 224 &RepositoryFileFromPathRequest {
207 225 rev: operation.rev,
@@ -223,6 +241,7 @@ pub fn repository_diff(
223 241 state: DatabaseBackend,
224 242 operation_state: StackOperationState,
225 243 backend: BackendWrapper,
244 requester: Option<AuthenticatedUser>,
226 245 ) -> BoxFuture<'static, Result<RepositoryDiff, OperationError<RepositoryError>>> {
227 246 let object = object.clone();
228 247
@@ -234,7 +253,7 @@ pub fn repository_diff(
234 253
235 254 let mut repository_backend = state.repository_backend.lock().await;
236 255 let diff = repository_backend
237 .repository_diff(None, object.object(), &operation)
256 .repository_diff(&requester, object.object(), &operation)
238 257 .await
239 258 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
240 259 drop(repository_backend);
@@ -250,6 +269,7 @@ pub fn repository_diff_patch(
250 269 state: DatabaseBackend,
251 270 operation_state: StackOperationState,
252 271 backend: BackendWrapper,
272 requester: Option<AuthenticatedUser>,
253 273 ) -> BoxFuture<'static, Result<String, OperationError<RepositoryError>>> {
254 274 let object = object.clone();
255 275
@@ -261,7 +281,7 @@ pub fn repository_diff_patch(
261 281
262 282 let mut repository_backend = state.repository_backend.lock().await;
263 283 let patch = repository_backend
264 .repository_diff_patch(None, object.object(), &operation)
284 .repository_diff_patch(&requester, object.object(), &operation)
265 285 .await
266 286 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
267 287 drop(repository_backend);
@@ -277,6 +297,7 @@ pub fn repository_commit_before(
277 297 state: DatabaseBackend,
278 298 operation_state: StackOperationState,
279 299 backend: BackendWrapper,
300 requester: Option<AuthenticatedUser>,
280 301 ) -> BoxFuture<'static, Result<Commit, OperationError<RepositoryError>>> {
281 302 let object = object.clone();
282 303
@@ -288,7 +309,7 @@ pub fn repository_commit_before(
288 309
289 310 let mut repository_backend = state.repository_backend.lock().await;
290 311 let file = repository_backend
291 .repository_commit_before(None, object.object(), &operation)
312 .repository_commit_before(&requester, object.object(), &operation)
292 313 .await
293 314 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
294 315 drop(repository_backend);
@@ -394,12 +415,13 @@ pub fn instance_create_repository_request(
394 415 _object: &Instance,
395 416 operation: RepositoryCreateRequest,
396 417 state: DatabaseBackend,
418 requester: AuthenticatedUser,
397 419 ) -> BoxFuture<'static, Result<Repository, OperationError<InstanceError>>> {
398 420 async move {
399 421 let mut backend = state.repository_backend.lock().await;
400 422
401 423 backend
402 .create_repository(&operation.owner, &operation)
424 .create_repository(&requester, &operation)
403 425 .await
404 426 .map_err(|e| OperationError::Internal(e.to_string()))
405 427 }

giterated-daemon/src/database_backend/mod.rs

View file
@@ -131,7 +131,7 @@ mod test {
131 131 use giterated_models::user::{DisplayName, User};
132 132 use giterated_models::value::{AnyValue, GiteratedObjectValue};
133 133 use giterated_stack::handler::GiteratedBackend;
134 use giterated_stack::StackOperationState;
134 use giterated_stack::{AuthenticatedUser, StackOperationState};
135 135 use serde_json::Value;
136 136 use tokio::sync::Mutex;
137 137
@@ -169,7 +169,7 @@ mod test {
169 169 }
170 170 async fn repositories_for_user(
171 171 &mut self,
172 _requester: Option<&User>,
172 _requester: &Option<AuthenticatedUser>,
173 173 _user: &User,
174 174 ) -> Result<Vec<RepositorySummary>, Error> {
175 175 todo!()
@@ -200,14 +200,14 @@ mod test {
200 200 impl RepositoryBackend for TestUserRepositoryBackend {
201 201 async fn create_repository(
202 202 &mut self,
203 _user: &User,
203 _user: &AuthenticatedUser,
204 204 _request: &RepositoryCreateRequest,
205 205 ) -> Result<Repository, GitBackendError> {
206 206 todo!()
207 207 }
208 208 async fn repository_file_inspect(
209 209 &mut self,
210 _requester: Option<&User>,
210 _requester: &Option<AuthenticatedUser>,
211 211 _repository: &Repository,
212 212 _request: &RepositoryFileInspectRequest,
213 213 ) -> Result<Vec<RepositoryTreeEntry>, Error> {
@@ -215,7 +215,7 @@ mod test {
215 215 }
216 216 async fn repository_file_from_id(
217 217 &mut self,
218 _requester: Option<&User>,
218 _requester: &Option<AuthenticatedUser>,
219 219 _repository: &Repository,
220 220 _request: &RepositoryFileFromIdRequest,
221 221 ) -> Result<RepositoryFile, Error> {
@@ -223,7 +223,7 @@ mod test {
223 223 }
224 224 async fn repository_file_from_path(
225 225 &mut self,
226 _requester: Option<&User>,
226 _requester: &Option<AuthenticatedUser>,
227 227 _repository: &Repository,
228 228 _request: &RepositoryFileFromPathRequest,
229 229 ) -> Result<RepositoryFile, Error> {
@@ -231,7 +231,7 @@ mod test {
231 231 }
232 232 async fn repository_diff(
233 233 &mut self,
234 _requester: Option<&User>,
234 _requester: &Option<AuthenticatedUser>,
235 235 _repository: &Repository,
236 236 _request: &RepositoryDiffRequest,
237 237 ) -> Result<RepositoryDiff, Error> {
@@ -239,7 +239,7 @@ mod test {
239 239 }
240 240 async fn repository_diff_patch(
241 241 &mut self,
242 _requester: Option<&User>,
242 _requester: &Option<AuthenticatedUser>,
243 243 _repository: &Repository,
244 244 _request: &RepositoryDiffPatchRequest,
245 245 ) -> Result<String, Error> {
@@ -247,7 +247,7 @@ mod test {
247 247 }
248 248 async fn repository_commit_before(
249 249 &mut self,
250 _requester: Option<&User>,
250 _requester: &Option<AuthenticatedUser>,
251 251 _repository: &Repository,
252 252 _request: &RepositoryCommitBeforeRequest,
253 253 ) -> Result<Commit, Error> {
@@ -282,7 +282,11 @@ mod test {
282 282 Ok(())
283 283 }
284 284
285 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> {
285 async fn exists(
286 &mut self,
287 _requester: &Option<AuthenticatedUser>,
288 repository: &Repository,
289 ) -> Result<bool, Error> {
286 290 // Ok(true)
287 291 Ok(repository
288 292 == &Repository::from_str(

giterated-models/src/repository/settings.rs

View file
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
2 2
3 3 use crate::{settings::Setting, user::User};
4 4
5 use super::{DefaultBranch, RepositoryVisibility};
5 use super::DefaultBranch;
6 6
7 7 impl Setting for DefaultBranch {
8 8 fn name() -> &'static str {

giterated-stack/src/lib.rs

View file
@@ -297,6 +297,57 @@ where
297 297 }
298 298 }
299 299
300 #[async_trait::async_trait]
301 impl<O, O1, O2, O3, D, F, S> GiteratedOperationHandler<(O1, O2, O3), O, D, S> for F
302 where
303 F: FnMut(
304 &O,
305 D,
306 S,
307 O1,
308 O2,
309 O3,
310 ) -> Pin<
311 Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>> + Send>,
312 > + Send
313 + Sync
314 + Clone,
315 O: GiteratedObject + Send + Sync,
316 D: GiteratedOperation<O> + 'static,
317 <D as GiteratedOperation<O>>::Failure: Send,
318 S: Send + Sync + Clone + 'static,
319 O1: FromOperationState,
320 O2: FromOperationState,
321 O3: FromOperationState,
322 {
323 fn operation_name(&self) -> &str {
324 D::operation_name()
325 }
326
327 fn object_name(&self) -> &str {
328 O::object_name()
329 }
330
331 async fn handle(
332 &self,
333 object: &O,
334 operation: D,
335 state: S,
336 operation_state: &StackOperationState,
337 ) -> Result<D::Success, OperationError<D::Failure>> {
338 let o1 = O1::from_state(operation_state)
339 .await
340 .map_err(|e| OperationError::Internal(e.to_string()))?;
341 let o2 = O2::from_state(operation_state)
342 .await
343 .map_err(|e| OperationError::Internal(e.to_string()))?;
344 let o3 = O3::from_state(operation_state)
345 .await
346 .map_err(|e| OperationError::Internal(e.to_string()))?;
347 self.clone()(object, operation, state, o1, o2, o3).await
348 }
349 }
350
300 351 pub struct OperationWrapper<S: Send + Sync + Clone> {
301 352 func: Box<
302 353 dyn Fn(
@@ -391,6 +442,43 @@ impl FromOperationState for StackOperationState {
391 442 }
392 443 }
393 444
445 #[async_trait::async_trait]
446 impl FromOperationState for AuthenticatedUser {
447 type Error = ();
448
449 async fn from_state(
450 state: &StackOperationState,
451 ) -> Result<AuthenticatedUser, OperationError<()>> {
452 state
453 .user
454 .clone()
455 .ok_or_else(|| OperationError::Operation(()))
456 }
457 }
458
459 #[async_trait::async_trait]
460 impl FromOperationState for AuthenticatedInstance {
461 type Error = ();
462
463 async fn from_state(
464 state: &StackOperationState,
465 ) -> Result<AuthenticatedInstance, OperationError<()>> {
466 state
467 .instance
468 .clone()
469 .ok_or_else(|| OperationError::Operation(()))
470 }
471 }
472
473 #[async_trait::async_trait]
474 impl<T: FromOperationState> FromOperationState for Option<T> {
475 type Error = ();
476
477 async fn from_state(state: &StackOperationState) -> Result<Option<T>, OperationError<()>> {
478 Ok(T::from_state(state).await.ok())
479 }
480 }
481
394 482 #[derive(Clone)]
395 483 pub struct StackOperationState {
396 484 pub giterated_backend: BackendWrapper,