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

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨13827⁩ bytes
Raw
1 pub mod handler;
2
3 use std::sync::Arc;
4
5 use giterated_models::error::OperationError;
6 use giterated_models::instance::Instance;
7 use giterated_models::object::{GiteratedObject, Object, ObjectRequestError};
8 use giterated_models::object_backend::ObjectBackend;
9 use giterated_models::operation::GiteratedOperation;
10 use giterated_models::repository::Repository;
11 use giterated_models::user::User;
12 use giterated_stack::handler::GiteratedBackend;
13 use giterated_stack::{OperationHandlers, StackOperationState};
14 use std::fmt::Debug;
15 use tokio::sync::Mutex;
16
17 use crate::backend::{RepositoryBackend, UserBackend};
18
19 use self::handler::{
20 instance_authentication_request, instance_create_repository_request,
21 instance_registration_request, repository_commit_before, repository_diff,
22 repository_diff_patch, repository_file_from_id, repository_file_from_path,
23 repository_get_setting, repository_get_value, repository_info, repository_set_setting,
24 user_get_repositories, user_get_setting, user_get_value, user_set_setting,
25 };
26
27 #[derive(Clone, Debug)]
28 pub struct Foobackend {}
29
30 #[async_trait::async_trait]
31 impl ObjectBackend<StackOperationState> for Foobackend {
32 async fn object_operation<O: GiteratedObject + Debug, D: GiteratedOperation<O> + Debug>(
33 &self,
34 _object: O,
35 _operation: &str,
36 _payload: D,
37 _operation_state: &StackOperationState,
38 ) -> Result<D::Success, OperationError<D::Failure>> {
39 // We don't handle operations with this backend
40 Err(OperationError::Unhandled)
41 }
42
43 async fn get_object<O: GiteratedObject + Debug>(
44 &self,
45 _object_str: &str,
46 _operation_state: &StackOperationState,
47 ) -> Result<Object<StackOperationState, O, Self>, OperationError<ObjectRequestError>> {
48 Err(OperationError::Unhandled)
49 }
50 }
51
52 /// A backend implementation which attempts to resolve data from the instance's database.
53 #[derive(Clone)]
54 pub struct DatabaseBackend {
55 pub(self) our_instance: Instance,
56 pub(self) user_backend: Arc<Mutex<dyn UserBackend + Send>>,
57 pub(self) repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
58 }
59
60 impl DatabaseBackend {
61 pub fn new(
62 instance: Instance,
63 user_backend: Arc<Mutex<dyn UserBackend + Send>>,
64 repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
65 ) -> Self {
66 Self {
67 our_instance: instance,
68 user_backend,
69 repository_backend,
70 }
71 }
72
73 pub fn into_backend(&self) -> GiteratedBackend<Self> {
74 let mut handlers = OperationHandlers::default();
75
76 handlers
77 .insert(user_get_repositories)
78 .insert(user_get_value)
79 .insert(user_get_setting)
80 .insert(user_set_setting)
81 .insert(repository_info)
82 .insert(repository_file_from_id)
83 .insert(repository_file_from_path)
84 .insert(repository_diff)
85 .insert(repository_diff_patch)
86 .insert(repository_commit_before)
87 .insert(repository_get_value)
88 .insert(repository_get_setting)
89 .insert(repository_set_setting)
90 .insert(instance_registration_request)
91 .insert(instance_authentication_request)
92 .insert(instance_create_repository_request)
93 .register_object::<Instance>()
94 .register_object::<Repository>()
95 .register_object::<User>();
96
97 GiteratedBackend::new(self.clone(), handlers)
98 }
99 }
100
101 impl Debug for DatabaseBackend {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("DatabaseBackend").finish()
104 }
105 }
106
107 // TODO: These should be on the stack
108 // These tests verify that the essential handling of the database backend is
109 // functional and correct.
110 #[cfg(test)]
111 mod test {
112 use std::{str::FromStr, sync::Arc};
113
114 use anyhow::Error;
115
116 use giterated_models::authenticated::UserAuthenticationToken;
117
118 use giterated_models::instance::{
119 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
120 };
121
122 use giterated_models::object_backend::ObjectBackend;
123
124 use giterated_models::repository::{
125 Commit, Description, Repository, RepositoryCommitBeforeRequest, RepositoryDiff,
126 RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile,
127 RepositoryFileFromIdRequest, RepositoryFileFromPathRequest, RepositoryFileInspectRequest,
128 RepositorySummary, RepositoryTreeEntry,
129 };
130 use giterated_models::settings::AnySetting;
131 use giterated_models::user::{DisplayName, User};
132 use giterated_models::value::{AnyValue, GiteratedObjectValue};
133 use giterated_stack::handler::GiteratedBackend;
134 use giterated_stack::{AuthenticatedUser, StackOperationState};
135 use serde_json::Value;
136 use tokio::sync::Mutex;
137
138 use crate::backend::{git::GitBackendError, AuthBackend, RepositoryBackend, UserBackend};
139
140 use super::DatabaseBackend;
141 pub struct TestUserDatabaseBackend;
142
143 #[async_trait::async_trait]
144 impl UserBackend for TestUserDatabaseBackend {
145 async fn get_value(&mut self, _user: &User, name: &str) -> Result<AnyValue<User>, Error> {
146 assert_eq!(name, DisplayName::value_name());
147
148 Ok(serde_json::from_slice(
149 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
150 )
151 .unwrap())
152 }
153 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
154 Ok(serde_json::from_slice(
155 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
156 )
157 .unwrap())
158 }
159 async fn write_setting(
160 &mut self,
161 _user: &User,
162 _name: &str,
163 _setting: &Value,
164 ) -> Result<(), Error> {
165 Ok(())
166 }
167 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
168 Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap())
169 }
170 async fn repositories_for_user(
171 &mut self,
172 _requester: &Option<AuthenticatedUser>,
173 _user: &User,
174 ) -> Result<Vec<RepositorySummary>, Error> {
175 todo!()
176 }
177 }
178
179 #[async_trait::async_trait]
180 impl AuthBackend for TestUserDatabaseBackend {
181 async fn register(
182 &mut self,
183 _request: RegisterAccountRequest,
184 ) -> Result<UserAuthenticationToken, Error> {
185 todo!()
186 }
187
188 async fn login(
189 &mut self,
190 _source: &Instance,
191 _request: AuthenticationTokenRequest,
192 ) -> Result<UserAuthenticationToken, Error> {
193 todo!()
194 }
195 }
196
197 pub struct TestUserRepositoryBackend;
198
199 #[async_trait::async_trait]
200 impl RepositoryBackend for TestUserRepositoryBackend {
201 async fn create_repository(
202 &mut self,
203 _user: &AuthenticatedUser,
204 _request: &RepositoryCreateRequest,
205 ) -> Result<Repository, GitBackendError> {
206 todo!()
207 }
208 async fn repository_file_inspect(
209 &mut self,
210 _requester: &Option<AuthenticatedUser>,
211 _repository: &Repository,
212 _request: &RepositoryFileInspectRequest,
213 ) -> Result<Vec<RepositoryTreeEntry>, Error> {
214 todo!()
215 }
216 async fn repository_file_from_id(
217 &mut self,
218 _requester: &Option<AuthenticatedUser>,
219 _repository: &Repository,
220 _request: &RepositoryFileFromIdRequest,
221 ) -> Result<RepositoryFile, Error> {
222 todo!()
223 }
224 async fn repository_file_from_path(
225 &mut self,
226 _requester: &Option<AuthenticatedUser>,
227 _repository: &Repository,
228 _request: &RepositoryFileFromPathRequest,
229 ) -> Result<RepositoryFile, Error> {
230 todo!()
231 }
232 async fn repository_diff(
233 &mut self,
234 _requester: &Option<AuthenticatedUser>,
235 _repository: &Repository,
236 _request: &RepositoryDiffRequest,
237 ) -> Result<RepositoryDiff, Error> {
238 todo!()
239 }
240 async fn repository_diff_patch(
241 &mut self,
242 _requester: &Option<AuthenticatedUser>,
243 _repository: &Repository,
244 _request: &RepositoryDiffPatchRequest,
245 ) -> Result<String, Error> {
246 todo!()
247 }
248 async fn repository_commit_before(
249 &mut self,
250 _requester: &Option<AuthenticatedUser>,
251 _repository: &Repository,
252 _request: &RepositoryCommitBeforeRequest,
253 ) -> Result<Commit, Error> {
254 todo!()
255 }
256 async fn get_value(
257 &mut self,
258 _repository: &Repository,
259 _name: &str,
260 ) -> Result<AnyValue<Repository>, Error> {
261 Ok(serde_json::from_slice(
262 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
263 )
264 .unwrap())
265 }
266 async fn get_setting(
267 &mut self,
268 _repository: &Repository,
269 _name: &str,
270 ) -> Result<AnySetting, Error> {
271 Ok(serde_json::from_slice(
272 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
273 )
274 .unwrap())
275 }
276 async fn write_setting(
277 &mut self,
278 _repository: &Repository,
279 _name: &str,
280 _setting: &Value,
281 ) -> Result<(), Error> {
282 Ok(())
283 }
284
285 async fn exists(
286 &mut self,
287 _requester: &Option<AuthenticatedUser>,
288 repository: &Repository,
289 ) -> Result<bool, Error> {
290 // Ok(true)
291 Ok(repository
292 == &Repository::from_str(
293 "test_user:test.giterated.dev/[email protected]",
294 )
295 .unwrap())
296 }
297 }
298
299 fn test_backend() -> GiteratedBackend<DatabaseBackend> {
300 DatabaseBackend {
301 our_instance: Instance::from_str("testing.giterated.dev").unwrap(),
302 user_backend: Arc::new(Mutex::new(TestUserDatabaseBackend)) as _,
303 repository_backend: Arc::new(Mutex::new(TestUserRepositoryBackend)) as _,
304 }
305 .into_backend()
306 }
307
308 fn operation_state() -> StackOperationState {
309 todo!()
310 }
311
312 #[tokio::test]
313 async fn test_user_get() {
314 let backend = test_backend();
315 let operation_state = operation_state();
316
317 let mut user = backend
318 .get_object::<User>("test_user:test.giterated.dev", &operation_state)
319 .await
320 .expect("object should have been returned");
321
322 user.get::<DisplayName>(&operation_state)
323 .await
324 .expect("object value should have been returned");
325 }
326
327 #[tokio::test]
328 async fn test_user_get_setting() {
329 let backend = test_backend();
330 let operation_state = operation_state();
331
332 let mut user = backend
333 .get_object::<User>("test_user:test.giterated.dev", &operation_state)
334 .await
335 .expect("object should have been returned");
336
337 user.get_setting::<DisplayName>(&operation_state)
338 .await
339 .expect("object value should have been returned");
340 }
341
342 #[tokio::test]
343 async fn test_user_set_setting() {
344 let backend = test_backend();
345 let operation_state = operation_state();
346
347 let mut user = backend
348 .get_object::<User>("test_user:test.giterated.dev", &operation_state)
349 .await
350 .expect("object should have been returned");
351
352 user.set_setting::<DisplayName>(DisplayName(String::from("test")), &operation_state)
353 .await
354 .expect("object value should have been returned");
355 }
356
357 #[tokio::test]
358 async fn test_respository_get() {
359 let backend = test_backend();
360 let operation_state = operation_state();
361
362 let mut repository = backend
363 .get_object::<Repository>(
364 "test_user:test.giterated.dev/[email protected]",
365 &operation_state,
366 )
367 .await
368 .expect("object should have been returned");
369
370 repository
371 .get::<Description>(&operation_state)
372 .await
373 .expect("object value should have been returned");
374 }
375
376 #[tokio::test]
377 async fn test_repository_get_setting() {
378 let backend = test_backend();
379 let operation_state = operation_state();
380
381 let mut repository = backend
382 .get_object::<Repository>(
383 "test_user:test.giterated.dev/[email protected]",
384 &operation_state,
385 )
386 .await
387 .expect("object should have been returned");
388
389 repository
390 .get_setting::<Description>(&operation_state)
391 .await
392 .expect("object value should have been returned");
393 }
394
395 #[tokio::test]
396 async fn test_repository_set_setting() {
397 let backend = test_backend();
398 let operation_state = operation_state();
399
400 let mut repository = backend
401 .get_object::<Repository>(
402 "test_user:test.giterated.dev/[email protected]",
403 &operation_state,
404 )
405 .await
406 .expect("object should have been returned");
407
408 repository
409 .set_setting::<Description>(Description(String::from("test")), &operation_state)
410 .await
411 .expect("object value should have been returned");
412 }
413 }
414