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

ambee/giterated

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

Fixed imports!

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨ef0e853

Showing ⁨⁨54⁩ changed files⁩ with ⁨⁨1755⁩ insertions⁩ and ⁨⁨1663⁩ deletions⁩

giterated-daemon/src/authentication.rs

View file
@@ -1,9 +1,10 @@
1 1 use anyhow::Error;
2 use giterated_models::model::{
3 authenticated::{UserAuthenticationToken, UserTokenMetadata},
4 instance::Instance,
5 user::User,
6 };
2 use giterated_models::authenticated::{UserAuthenticationToken, UserTokenMetadata};
3
4 use giterated_models::instance::Instance;
5
6 use giterated_models::user::User;
7
7 8 use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, TokenData, Validation};
8 9 use std::{sync::Arc, time::SystemTime};
9 10 use tokio::{fs::File, io::AsyncReadExt, sync::Mutex};

giterated-daemon/src/authorization.rs

View file
@@ -0,0 +1,134 @@
1 use crate::connection::wrapper::ConnectionState;
2 use giterated_models::error::OperationError;
3
4 use giterated_models::object::GiteratedObject;
5
6 use giterated_models::repository::{
7 Repository, RepositoryFileInspectRequest, RepositoryIssueLabelsRequest,
8 RepositoryIssuesCountRequest, RepositoryIssuesRequest,
9 };
10
11 use giterated_models::user::User;
12
13 use giterated_models::{
14 object::ObjectRequest,
15 settings::{SetSetting, Setting},
16 value::{GetValue, GiteratedObjectValue},
17 };
18 #[async_trait::async_trait]
19 pub trait AuthorizedOperation<O: GiteratedObject, S: Send + Sync> {
20 /// Authorizes the operation, returning whether the operation was
21 /// authorized or not.
22 async fn authorize(
23 &self,
24 authenticating_user: Option<&User>,
25 object: &O,
26 state: &mut S,
27 ) -> Result<bool, OperationError<()>>;
28 }
29
30 #[async_trait::async_trait]
31 impl<S: Setting + Send + Sync> AuthorizedOperation<User, ConnectionState> for SetSetting<S> {
32 async fn authorize(
33 &self,
34 _authenticating_user: Option<&User>,
35 _object: &User,
36 _state: &mut ConnectionState,
37 ) -> Result<bool, OperationError<()>> {
38 // TODO
39 Ok(true)
40 }
41 }
42
43 #[async_trait::async_trait]
44 impl<S: Setting + Send + Sync> AuthorizedOperation<Repository, ConnectionState> for SetSetting<S> {
45 async fn authorize(
46 &self,
47 _authenticating_user: Option<&User>,
48 _object: &Repository,
49 _state: &mut ConnectionState,
50 ) -> Result<bool, OperationError<()>> {
51 // TODO
52 Ok(true)
53 }
54 }
55
56 #[async_trait::async_trait]
57 impl<V: GiteratedObjectValue + Send + Sync> AuthorizedOperation<Repository, ConnectionState>
58 for GetValue<V>
59 {
60 async fn authorize(
61 &self,
62 _authenticating_user: Option<&User>,
63 _object: &Repository,
64 _state: &mut ConnectionState,
65 ) -> Result<bool, OperationError<()>> {
66 // TODO
67 Ok(true)
68 }
69 }
70
71 #[async_trait::async_trait]
72 impl AuthorizedOperation<Repository, ConnectionState> for ObjectRequest {
73 async fn authorize(
74 &self,
75 _authenticating_user: Option<&User>,
76 _object: &Repository,
77 _state: &mut ConnectionState,
78 ) -> Result<bool, OperationError<()>> {
79 // TODO
80 Ok(true)
81 }
82 }
83
84 #[async_trait::async_trait]
85 impl AuthorizedOperation<Repository, ConnectionState> for RepositoryFileInspectRequest {
86 async fn authorize(
87 &self,
88 _authenticating_user: Option<&User>,
89 _object: &Repository,
90 _state: &mut ConnectionState,
91 ) -> Result<bool, OperationError<()>> {
92 // TODO
93 Ok(true)
94 }
95 }
96
97 #[async_trait::async_trait]
98 impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssuesRequest {
99 async fn authorize(
100 &self,
101 _authenticating_user: Option<&User>,
102 _object: &Repository,
103 _state: &mut ConnectionState,
104 ) -> Result<bool, OperationError<()>> {
105 // TODO
106 Ok(true)
107 }
108 }
109
110 #[async_trait::async_trait]
111 impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssueLabelsRequest {
112 async fn authorize(
113 &self,
114 _authenticating_user: Option<&User>,
115 _object: &Repository,
116 _state: &mut ConnectionState,
117 ) -> Result<bool, OperationError<()>> {
118 // TODO
119 Ok(true)
120 }
121 }
122
123 #[async_trait::async_trait]
124 impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssuesCountRequest {
125 async fn authorize(
126 &self,
127 _authenticating_user: Option<&User>,
128 _object: &Repository,
129 _state: &mut ConnectionState,
130 ) -> Result<bool, OperationError<()>> {
131 // TODO
132 Ok(true)
133 }
134 }

giterated-daemon/src/backend/git.rs

View file
@@ -2,25 +2,17 @@ use anyhow::Error;
2 2 use async_trait::async_trait;
3 3 use futures_util::StreamExt;
4 4
5 use giterated_models::{
6 model::{
7 instance::Instance,
8 repository::{
9 Commit, IssueLabel, Repository, RepositoryIssue, RepositorySummary,
10 RepositoryTreeEntry, RepositoryVisibility,
11 },
12 settings::{AnySetting, RepositoryDescription, RepositoryVisibilitySetting, Setting},
13 user::{User, UserParseError},
14 },
15 operation::{
16 instance::RepositoryCreateRequest,
17 repository::{
18 RepositoryFileInspectRequest, RepositoryIssueLabelsRequest,
19 RepositoryIssuesCountRequest, RepositoryIssuesRequest,
20 },
21 },
22 values::AnyValue,
5 use giterated_models::instance::{Instance, RepositoryCreateRequest};
6
7 use giterated_models::repository::{
8 Commit, IssueLabel, Repository, RepositoryDescription, RepositoryFileInspectRequest,
9 RepositoryIssue, RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest,
10 RepositoryIssuesRequest, RepositorySummary, RepositoryTreeEntry, RepositoryVisibility,
11 RepositoryVisibilitySetting,
23 12 };
13 use giterated_models::settings::{AnySetting, Setting};
14 use giterated_models::user::{User, UserParseError};
15 use giterated_models::value::AnyValue;
24 16 use serde_json::Value;
25 17 use sqlx::{Either, PgPool};
26 18 use std::{

giterated-daemon/src/backend/mod.rs

View file
@@ -9,25 +9,20 @@ use async_trait::async_trait;
9 9 use serde_json::Value;
10 10
11 11 use crate::backend::git::GitBackendError;
12 use giterated_models::{
13 model::{
14 authenticated::UserAuthenticationToken,
15 instance::Instance,
16 repository::{
17 IssueLabel, Repository, RepositoryIssue, RepositorySummary, RepositoryTreeEntry,
18 },
19 settings::AnySetting,
20 user::User,
21 },
22 operation::{
23 instance::{AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest},
24 repository::{
25 RepositoryFileInspectRequest, RepositoryIssueLabelsRequest,
26 RepositoryIssuesCountRequest, RepositoryIssuesRequest,
27 },
28 },
29 values::AnyValue,
12 use giterated_models::authenticated::UserAuthenticationToken;
13
14 use giterated_models::instance::{
15 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
16 };
17
18 use giterated_models::repository::{
19 IssueLabel, Repository, RepositoryFileInspectRequest, RepositoryIssue,
20 RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest,
21 RepositorySummary, RepositoryTreeEntry,
30 22 };
23 use giterated_models::settings::AnySetting;
24 use giterated_models::user::User;
25 use giterated_models::value::AnyValue;
31 26
32 27 #[async_trait]
33 28 pub trait RepositoryBackend {

giterated-daemon/src/backend/settings.rs

View file
@@ -1,6 +1,8 @@
1 1 use anyhow::Error;
2 2
3 use giterated_models::model::{repository::Repository, settings::AnySetting, user::User};
3 use giterated_models::repository::Repository;
4 use giterated_models::settings::AnySetting;
5 use giterated_models::user::User;
4 6
5 7 use sqlx::PgPool;
6 8
@@ -12,73 +14,62 @@ pub struct DatabaseSettings {
12 14
13 15 #[async_trait::async_trait]
14 16 impl MetadataBackend for DatabaseSettings {
15 async fn user_get(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
16 todo!()
17 async fn user_get(&mut self, user: &User, name: &str) -> Result<AnySetting, Error> {
18 let row = sqlx::query_as!(
19 UserSettingRow,
20 "SELECT * FROM user_settings WHERE username = $1 AND name = $2",
21 user.username,
22 name
23 )
24 .fetch_one(&self.pg_pool)
25 .await?;
26
27 let setting = serde_json::from_str(&row.value)?;
28
29 Ok(setting)
17 30 }
18 31 async fn user_write(
19 32 &mut self,
20 _user: &User,
21 _name: &str,
22 _value: AnySetting,
33 user: &User,
34 name: &str,
35 value: AnySetting,
23 36 ) -> Result<(), Error> {
24 // for (name, value) in settings {
25 // sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
26 // user.username, name, value)
27 // .execute(&self.pg_pool).await?;
28 // }
37 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
38 user.username, name, serde_json::to_string(&value)?)
39 .execute(&self.pg_pool).await?;
29 40
30 // Ok(())
31
32 todo!()
41 Ok(())
33 42 }
34 43
35 44 async fn repository_get(
36 45 &mut self,
37 _repository: &Repository,
38 _name: &str,
46 repository: &Repository,
47 name: &str,
39 48 ) -> Result<AnySetting, Error> {
40 // let settings = sqlx::query_as!(
41 // RepositorySettingRow,
42 // r#"SELECT * FROM repository_settings WHERE repository = $1"#,
43 // repository.to_string()
44 // )
45 // .fetch_many(&self.pg_pool)
46 // .filter_map(|result| async move {
47 // if let Ok(Either::Right(row)) = result {
48 // Some(row)
49 // } else {
50 // None
51 // }
52 // })
53 // .filter_map(|row| async move {
54 // if let Ok(value) = serde_json::from_str(&row.value) {
55 // Some((row.name, value))
56 // } else {
57 // None
58 // }
59 // })
60 // .collect::<Vec<_>>()
61 // .await;
49 let row = sqlx::query_as!(
50 RepositorySettingRow,
51 "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2",
52 format!("{}/{}", repository.owner, repository.name),
53 name
54 )
55 .fetch_one(&self.pg_pool)
56 .await?;
62 57
63 // Ok(settings)
58 let setting = serde_json::from_str(&row.value)?;
64 59
65 todo!()
60 Ok(setting)
66 61 }
67 62 async fn repository_write(
68 63 &mut self,
69 _repository: &Repository,
70 _name: &str,
71 _value: AnySetting,
64 repository: &Repository,
65 name: &str,
66 value: AnySetting,
72 67 ) -> Result<(), Error> {
73 // for (name, value) in settings {
74 // sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
75 // repository.to_string(), name, value)
76 // .execute(&self.pg_pool).await?;
77 // }
78
79 // Ok(())
68 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
69 format!("{}/{}", repository.owner, repository.name), name, serde_json::to_string(&value)?)
70 .execute(&self.pg_pool).await?;
80 71
81 todo!()
72 Ok(())
82 73 }
83 74 }
84 75

giterated-daemon/src/backend/user.rs

View file
@@ -1,20 +1,17 @@
1 use std::sync::Arc;
2
3 1 use anyhow::Error;
2 use giterated_models::authenticated::UserAuthenticationToken;
3
4 use giterated_models::instance::{AuthenticationTokenRequest, Instance, RegisterAccountRequest};
5
6 use giterated_models::settings::{AnySetting, Setting};
7 use giterated_models::user::{User, UserBio, UserDisplayName, UserParseError};
8 use giterated_models::value::AnyValue;
9 use std::sync::Arc;
4 10
5 11 use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, Key, KeyInit};
6 12 use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
7 13 use base64::{engine::general_purpose::STANDARD, Engine as _};
8 use giterated_models::{
9 model::{
10 authenticated::UserAuthenticationToken,
11 instance::Instance,
12 settings::{AnySetting, Setting, UserBio, UserDisplayName},
13 user::{User, UserParseError},
14 },
15 operation::instance::{AuthenticationTokenRequest, RegisterAccountRequest},
16 values::AnyValue,
17 };
14
18 15 use rsa::{
19 16 pkcs8::{EncodePrivateKey, EncodePublicKey},
20 17 rand_core::OsRng,

giterated-daemon/src/cache_backend.rs

View file
@@ -1,7 +1,9 @@
1 use giterated_models::{
2 error::OperationError,
3 operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError},
4 };
1 use giterated_models::error::OperationError;
2
3 use giterated_models::object::{GiteratedObject, Object, ObjectRequestError};
4 use giterated_models::object_backend::ObjectBackend;
5 use giterated_models::operation::GiteratedOperation;
6
5 7 use std::fmt::Debug;
6 8
7 9 #[derive(Clone, Debug)]

giterated-daemon/src/connection.rs

View file
@@ -1,14 +1,12 @@
1 // pub mod authentication;
2 // pub mod forwarded;
3 // pub mod handshake;
4 // pub mod repository;
5 // pub mod user;
6 1 pub mod wrapper;
7 2
3 use giterated_models::instance::Instance;
4
5 use giterated_models::instance::InstanceMeta;
6
8 7 use std::{any::type_name, collections::HashMap};
9 8
10 9 use anyhow::Error;
11 use giterated_models::model::instance::{Instance, InstanceMeta};
12 10 use serde::{de::DeserializeOwned, Serialize};
13 11 use tokio::{net::TcpStream, task::JoinHandle};
14 12 use tokio_tungstenite::WebSocketStream;

giterated-daemon/src/connection/wrapper.rs

View file
@@ -5,11 +5,15 @@ use std::{
5 5
6 6 use anyhow::Error;
7 7 use futures_util::{SinkExt, StreamExt};
8
9 use giterated_models::instance::Instance;
10
11 use giterated_models::object_backend::ObjectBackend;
12
8 13 use giterated_models::{
9 model::{authenticated::AuthenticatedPayload, instance::Instance},
10 operation::{AnyObject, AnyOperation, GiteratedMessage, ObjectBackend},
14 authenticated::AuthenticatedPayload, message::GiteratedMessage, object::AnyObject,
15 operation::AnyOperation,
11 16 };
12
13 17 use serde::Serialize;
14 18
15 19 use tokio::{net::TcpStream, sync::Mutex};

giterated-daemon/src/database_backend/handler.rs

View file
@@ -3,9 +3,12 @@ use std::{collections::HashMap, pin::Pin, sync::Arc};
3 3 use futures_util::{future::BoxFuture, Future, FutureExt};
4 4 use giterated_models::{
5 5 error::{GetValueError, OperationError},
6 model::{repository::Repository, settings::AnySetting, user::User},
7 operation::{AnyObject, AnyOperation, GetValue, GiteratedObject, GiteratedOperation},
8 values::{AnyValue, GetSetting, GetSettingError, SetSetting, SetSettingError},
6 object::{AnyObject, GiteratedObject},
7 operation::{AnyOperation, GiteratedOperation},
8 repository::Repository,
9 settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError},
10 user::User,
11 value::{AnyValue, GetValue},
9 12 };
10 13
11 14 use super::DatabaseBackend;

giterated-daemon/src/database_backend/mod.rs

View file
@@ -2,11 +2,13 @@ pub mod handler;
2 2
3 3 use std::{str::FromStr, sync::Arc};
4 4
5 use giterated_models::{
6 error::OperationError,
7 model::{instance::Instance, repository::Repository, user::User},
8 operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError},
9 };
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;
10 12 use std::fmt::Debug;
11 13 use tokio::sync::Mutex;
12 14
@@ -182,27 +184,22 @@ mod test {
182 184 use std::{str::FromStr, sync::Arc};
183 185
184 186 use anyhow::Error;
185 use giterated_models::model::settings::UserDisplayName;
186 use giterated_models::operation::ObjectBackend;
187 use giterated_models::values::repository::Description;
188 use giterated_models::{
189 model::{
190 authenticated::UserAuthenticationToken,
191 instance::Instance,
192 repository::{Repository, RepositorySummary, RepositoryTreeEntry},
193 settings::AnySetting,
194 user::User,
195 },
196 operation::{
197 instance::{
198 AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest,
199 },
200 repository::RepositoryFileInspectRequest,
201 GiteratedObjectValue,
202 },
203 values::{user::DisplayName, AnyValue},
187
188 use giterated_models::authenticated::UserAuthenticationToken;
189
190 use giterated_models::instance::{
191 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
204 192 };
205 193
194 use giterated_models::object_backend::ObjectBackend;
195
196 use giterated_models::repository::{
197 Description, Repository, RepositoryFileInspectRequest, RepositorySummary,
198 RepositoryTreeEntry,
199 };
200 use giterated_models::settings::AnySetting;
201 use giterated_models::user::{DisplayName, User, UserDisplayName};
202 use giterated_models::value::{AnyValue, GiteratedObjectValue};
206 203 use serde_json::Value;
207 204 use tokio::sync::Mutex;
208 205

giterated-daemon/src/federation/connections.rs

View file
@@ -2,7 +2,7 @@ use std::collections::HashMap;
2 2
3 3 use anyhow::Error;
4 4 use giterated_api::DaemonConnectionPool;
5 use giterated_models::model::instance::Instance;
5 use giterated_models::instance::Instance;
6 6
7 7 #[derive(Default)]
8 8 pub struct InstanceConnections {

giterated-daemon/src/keys.rs

View file
@@ -1,7 +1,8 @@
1 use std::collections::HashMap;
2
3 1 use anyhow::Error;
4 use giterated_models::model::instance::Instance;
2
3 use giterated_models::instance::Instance;
4
5 use std::collections::HashMap;
5 6
6 7 #[derive(Default)]
7 8 pub struct PublicKeyCache {

giterated-daemon/src/lib.rs

View file
@@ -3,6 +3,7 @@ use std::str::FromStr;
3 3 use semver::{Version, VersionReq};
4 4
5 5 pub mod authentication;
6 pub mod authorization;
6 7 pub mod backend;
7 8 pub mod cache_backend;
8 9 pub mod connection;

giterated-daemon/src/main.rs

View file
@@ -8,7 +8,9 @@ use giterated_daemon::{
8 8 connection::{self, wrapper::connection_wrapper},
9 9 federation::connections::InstanceConnections,
10 10 };
11 use giterated_models::model::instance::Instance;
11
12 use giterated_models::instance::Instance;
13
12 14 use sqlx::{postgres::PgConnectOptions, ConnectOptions, PgPool};
13 15 use std::{net::SocketAddr, str::FromStr, sync::Arc};
14 16 use tokio::{

giterated-daemon/src/message.rs

View file
@@ -1,11 +1,12 @@
1 use std::{fmt::Debug, ops::Deref};
2
3 1 use anyhow::Error;
4 2 use futures_util::Future;
5 use giterated_models::model::{
6 authenticated::{AuthenticatedPayload, AuthenticationSource, UserTokenMetadata},
7 instance::Instance,
8 user::User,
3
4 use giterated_models::instance::Instance;
5
6 use giterated_models::user::User;
7
8 use giterated_models::authenticated::{
9 AuthenticatedPayload, AuthenticationSource, UserTokenMetadata,
9 10 };
10 11 use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation};
11 12 use rsa::{
@@ -16,6 +17,7 @@ use rsa::{
16 17 RsaPublicKey,
17 18 };
18 19 use serde::{de::DeserializeOwned, Serialize};
20 use std::{fmt::Debug, ops::Deref};
19 21
20 22 use crate::connection::wrapper::ConnectionState;
21 23

giterated-models/src/authenticated.rs

View file
@@ -0,0 +1,201 @@
1 use std::fmt::Debug;
2
3 use rsa::{
4 pkcs1::DecodeRsaPrivateKey,
5 pss::SigningKey,
6 sha2::Sha256,
7 signature::{RandomizedSigner, SignatureEncoding},
8 RsaPrivateKey,
9 };
10 use serde::{Deserialize, Serialize};
11 use serde_json::Value;
12 use tracing::info;
13
14 use crate::{
15 instance::Instance,
16 message::GiteratedMessage,
17 object::{AnyObject, GiteratedObject},
18 operation::{AnyOperation, GiteratedOperation},
19 user::User,
20 };
21
22 #[derive(Debug, Serialize, Deserialize)]
23 pub struct UserTokenMetadata {
24 pub user: User,
25 pub generated_for: Instance,
26 pub exp: u64,
27 }
28
29 #[derive(Debug)]
30 pub struct Authenticated<O: GiteratedObject, D: GiteratedOperation<O>> {
31 pub source: Vec<Box<dyn AuthenticationSourceProvider + Send + Sync>>,
32 pub message: GiteratedMessage<O, D>,
33 }
34
35 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
36 pub struct AuthenticatedPayload {
37 pub source: Vec<AuthenticationSource>,
38 pub object: String,
39 pub operation: String,
40 pub payload: Vec<u8>,
41 }
42
43 impl AuthenticatedPayload {
44 pub fn into_message(self) -> GiteratedMessage<AnyObject, AnyOperation> {
45 GiteratedMessage {
46 object: AnyObject(self.object),
47 operation: self.operation,
48 payload: AnyOperation(serde_json::from_slice::<Value>(&self.payload).unwrap()),
49 }
50 }
51 }
52
53 pub trait AuthenticationSourceProvider: Debug {
54 fn authenticate(&self, payload: &Vec<u8>) -> AuthenticationSource;
55 }
56
57 pub trait AuthenticationSourceProviders: Debug {
58 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource>;
59 }
60
61 impl<A> AuthenticationSourceProviders for A
62 where
63 A: AuthenticationSourceProvider,
64 {
65 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource> {
66 vec![self.authenticate(payload)]
67 }
68 }
69
70 impl<A, B> AuthenticationSourceProviders for (A, B)
71 where
72 A: AuthenticationSourceProvider,
73 B: AuthenticationSourceProvider,
74 {
75 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource> {
76 let (first, second) = self;
77
78 vec![first.authenticate(payload), second.authenticate(payload)]
79 }
80 }
81
82 impl<O: GiteratedObject, D: GiteratedOperation<O>> Authenticated<O, D> {
83 pub fn new(message: GiteratedMessage<O, D>) -> Self {
84 Self {
85 source: vec![],
86 message,
87 }
88 }
89
90 pub fn append_authentication<P: AuthenticationSourceProvider + 'static + Send + Sync>(
91 &mut self,
92 authentication: P,
93 ) {
94 let message_payload = serde_json::to_vec(&self.message).unwrap();
95
96 info!(
97 "Verifying payload: {}",
98 std::str::from_utf8(&message_payload).unwrap()
99 );
100
101 self.source
102 .push(Box::new(authentication) as Box<dyn AuthenticationSourceProvider + Send + Sync>);
103 }
104
105 pub fn into_payload(mut self) -> AuthenticatedPayload {
106 let payload = bincode::serialize(&self.message.payload).unwrap();
107
108 AuthenticatedPayload {
109 object: self.message.object.to_string(),
110 operation: self.message.operation,
111 source: self
112 .source
113 .drain(..)
114 .map(|provider| provider.as_ref().authenticate(&payload))
115 .collect::<Vec<_>>(),
116 payload,
117 }
118 }
119 }
120
121 mod verified {}
122
123 #[derive(Clone, Debug)]
124 pub struct UserAuthenticator {
125 pub user: User,
126 pub token: UserAuthenticationToken,
127 }
128
129 impl AuthenticationSourceProvider for UserAuthenticator {
130 fn authenticate(&self, _payload: &Vec<u8>) -> AuthenticationSource {
131 AuthenticationSource::User {
132 user: self.user.clone(),
133 token: self.token.clone(),
134 }
135 }
136 }
137
138 #[derive(Debug, Clone)]
139 pub struct InstanceAuthenticator {
140 pub instance: Instance,
141 pub private_key: String,
142 }
143
144 impl AuthenticationSourceProvider for InstanceAuthenticator {
145 fn authenticate(&self, payload: &Vec<u8>) -> AuthenticationSource {
146 let mut rng = rand::thread_rng();
147
148 let private_key = RsaPrivateKey::from_pkcs1_pem(&self.private_key).unwrap();
149 let signing_key = SigningKey::<Sha256>::new(private_key);
150 let signature = signing_key.sign_with_rng(&mut rng, payload);
151
152 AuthenticationSource::Instance {
153 instance: self.instance.clone(),
154 // TODO: Actually parse signature from private key
155 signature: InstanceSignature(signature.to_bytes().into_vec()),
156 }
157 }
158 }
159
160 #[repr(transparent)]
161 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
162 pub struct UserAuthenticationToken(String);
163
164 impl From<String> for UserAuthenticationToken {
165 fn from(value: String) -> Self {
166 Self(value)
167 }
168 }
169
170 impl ToString for UserAuthenticationToken {
171 fn to_string(&self) -> String {
172 self.0.clone()
173 }
174 }
175
176 impl AsRef<str> for UserAuthenticationToken {
177 fn as_ref(&self) -> &str {
178 &self.0
179 }
180 }
181
182 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
183 pub struct InstanceSignature(Vec<u8>);
184
185 impl AsRef<[u8]> for InstanceSignature {
186 fn as_ref(&self) -> &[u8] {
187 &self.0
188 }
189 }
190
191 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
192 pub enum AuthenticationSource {
193 User {
194 user: User,
195 token: UserAuthenticationToken,
196 },
197 Instance {
198 instance: Instance,
199 signature: InstanceSignature,
200 },
201 }

giterated-models/src/discovery.rs

View file
@@ -0,0 +1,15 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{instance::Instance, repository::Repository};
4
5 #[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
6 pub enum DiscoveryItem {
7 Instance {
8 instance: Instance,
9 signature: Vec<u8>,
10 },
11 Repository {
12 repository: Repository,
13 signature: Vec<u8>,
14 },
15 }

giterated-models/src/handshake.rs

View file
@@ -1,7 +1,7 @@
1 1 use semver::Version;
2 2 use serde::{Deserialize, Serialize};
3 3
4 use crate::model::instance::Instance;
4 use crate::instance::Instance;
5 5
6 6 /// Sent by the initiator of a new inter-daemon connection.
7 7 #[derive(Clone, Debug, Serialize, Deserialize)]

giterated-models/src/instance/mod.rs

View file
@@ -0,0 +1,69 @@
1 use std::{fmt::Display, str::FromStr};
2
3 use serde::{Deserialize, Serialize};
4 use thiserror::Error;
5
6 mod operations;
7 mod values;
8
9 pub use operations::*;
10 pub use values::*;
11
12 use crate::object::GiteratedObject;
13
14 pub struct InstanceMeta {
15 pub url: String,
16 pub public_key: String,
17 }
18
19 /// An instance, defined by the URL it can be reached at.
20 ///
21 /// # Textual Format
22 /// An instance's textual format is its URL.
23 ///
24 /// ## Examples
25 /// For the instance `giterated.dev`, the following [`Instance`] initialization
26 /// would be valid:
27 ///
28 /// ```
29 /// let instance = Instance {
30 /// url: String::from("giterated.dev")
31 /// };
32 ///
33 /// // This is correct
34 /// assert_eq!(Instance::from_str("giterated.dev").unwrap(), instance);
35 /// ```
36 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
37 pub struct Instance {
38 pub url: String,
39 }
40
41 impl GiteratedObject for Instance {
42 fn object_name() -> &'static str {
43 "instance"
44 }
45
46 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
47 Ok(Instance::from_str(object_str).unwrap())
48 }
49 }
50
51 impl Display for Instance {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.write_str(&self.url)
54 }
55 }
56
57 impl FromStr for Instance {
58 type Err = InstanceParseError;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 Ok(Self { url: s.to_string() })
62 }
63 }
64
65 #[derive(Debug, Error)]
66 pub enum InstanceParseError {
67 #[error("invalid format")]
68 InvalidFormat,
69 }

giterated-models/src/instance/operations.rs

View file
@@ -0,0 +1,179 @@
1 use secrecy::Secret;
2 use serde::{Deserialize, Serialize};
3
4 use crate::{
5 authenticated::UserAuthenticationToken,
6 error::{InstanceError, OperationError},
7 object::Object,
8 object_backend::ObjectBackend,
9 operation::GiteratedOperation,
10 repository::{Repository, RepositoryVisibility},
11 user::{Password, User},
12 };
13
14 use super::Instance;
15
16 /// An account registration request.
17 ///
18 /// # Authentication
19 /// - Instance Authentication
20 /// - **ONLY ACCEPTED WHEN SAME-INSTANCE**
21 #[derive(Clone, Debug, Serialize, Deserialize)]
22 pub struct RegisterAccountRequest {
23 pub username: String,
24 pub email: Option<String>,
25 pub password: Secret<Password>,
26 }
27
28 impl GiteratedOperation<Instance> for RegisterAccountRequest {
29 type Success = UserAuthenticationToken;
30 type Failure = InstanceError;
31 }
32
33 #[derive(Clone, Debug, Serialize, Deserialize)]
34 pub struct RegisterAccountResponse {
35 pub token: String,
36 }
37
38 /// An authentication token request.
39 ///
40 /// AKA Login Request
41 ///
42 /// # Authentication
43 /// - Instance Authentication
44 /// - Identifies the Instance to issue the token for
45 /// # Authorization
46 /// - Credentials ([`crate::backend::AuthBackend`]-based)
47 /// - Identifies the User account to issue a token for
48 /// - Decrypts user private key to issue to
49 #[derive(Clone, Debug, Serialize, Deserialize)]
50 pub struct AuthenticationTokenRequest {
51 pub instance: Instance,
52 pub username: String,
53 pub password: Secret<Password>,
54 }
55
56 impl GiteratedOperation<Instance> for AuthenticationTokenRequest {
57 type Success = UserAuthenticationToken;
58 type Failure = InstanceError;
59 }
60
61 /// An authentication token extension request.
62 ///
63 /// # Authentication
64 /// - Instance Authentication
65 /// - Identifies the Instance to issue the token for
66 /// - User Authentication
67 /// - Authenticates the validity of the token
68 /// # Authorization
69 /// - Token-based
70 /// - Validates authorization using token's authenticity
71 #[derive(Clone, Debug, Serialize, Deserialize)]
72 pub struct TokenExtensionRequest {
73 pub token: UserAuthenticationToken,
74 }
75
76 impl GiteratedOperation<Instance> for TokenExtensionRequest {
77 type Success = Option<UserAuthenticationToken>;
78 type Failure = InstanceError;
79 }
80
81 /// A request to create a repository.
82 ///
83 /// # Authentication
84 /// - Instance Authentication
85 /// - Used to validate User token `issued_for`
86 /// - User Authentication
87 /// - Used to source owning user
88 /// - Used to authorize user token against user's instance
89 /// # Authorization
90 /// - Instance Authorization
91 /// - Used to authorize action using User token requiring a correct `issued_for` and valid issuance from user's instance
92 /// - User Authorization
93 /// - Potential User permissions checks
94 #[derive(Clone, Debug, Serialize, Deserialize)]
95 pub struct RepositoryCreateRequest {
96 pub instance: Option<Instance>,
97 pub name: String,
98 pub description: Option<String>,
99 pub visibility: RepositoryVisibility,
100 pub default_branch: String,
101 pub owner: User,
102 }
103
104 impl GiteratedOperation<Instance> for RepositoryCreateRequest {
105 type Success = Repository;
106 type Failure = InstanceError;
107 }
108
109 impl<B: ObjectBackend + std::fmt::Debug> Object<'_, Instance, B> {
110 pub async fn register_account(
111 &mut self,
112 email: Option<&str>,
113 username: &str,
114 password: &Secret<Password>,
115 ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> {
116 self.request::<RegisterAccountRequest>(RegisterAccountRequest {
117 username: username.to_string(),
118 email: email.map(|s| s.to_string()),
119 password: password.clone(),
120 })
121 .await
122 }
123
124 pub async fn authentication_token(
125 &mut self,
126 username: &str,
127 password: &Secret<Password>,
128 ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> {
129 self.request::<AuthenticationTokenRequest>(AuthenticationTokenRequest {
130 instance: self.inner.clone(),
131 username: username.to_string(),
132 password: password.clone(),
133 })
134 .await
135 }
136
137 pub async fn authentication_token_for(
138 &mut self,
139 instance: &Instance,
140 username: &str,
141 password: &Secret<Password>,
142 ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> {
143 self.request::<AuthenticationTokenRequest>(AuthenticationTokenRequest {
144 instance: instance.clone(),
145 username: username.to_string(),
146 password: password.clone(),
147 })
148 .await
149 }
150
151 pub async fn token_extension(
152 &mut self,
153 token: &UserAuthenticationToken,
154 ) -> Result<Option<UserAuthenticationToken>, OperationError<InstanceError>> {
155 self.request::<TokenExtensionRequest>(TokenExtensionRequest {
156 token: token.clone(),
157 })
158 .await
159 }
160
161 pub async fn create_repository(
162 &mut self,
163 instance: &Instance,
164 name: &str,
165 visibility: RepositoryVisibility,
166 default_branch: &str,
167 owner: &User,
168 ) -> Result<Repository, OperationError<InstanceError>> {
169 self.request::<RepositoryCreateRequest>(RepositoryCreateRequest {
170 instance: Some(instance.clone()),
171 name: name.to_string(),
172 description: None,
173 visibility,
174 default_branch: default_branch.to_string(),
175 owner: owner.clone(),
176 })
177 .await
178 }
179 }

giterated-models/src/instance/values.rs

View file
@@ -0,0 +1 @@
1

giterated-models/src/lib.rs

View file
@@ -1,5 +1,13 @@
1 pub mod authenticated;
2 pub mod discovery;
1 3 pub mod error;
2 4 pub mod handshake;
3 pub mod model;
5 pub mod instance;
6 pub mod message;
7 pub mod object;
8 pub mod object_backend;
4 9 pub mod operation;
5 pub mod values;
10 pub mod repository;
11 pub mod settings;
12 pub mod user;
13 pub mod value;

giterated-models/src/message.rs

View file
@@ -0,0 +1,69 @@
1 use serde::Serialize;
2
3 use crate::{
4 object::{AnyObject, GiteratedObject},
5 operation::{AnyOperation, GiteratedOperation},
6 };
7 use std::fmt::Debug;
8
9 #[derive(Serialize)]
10 #[serde(bound(deserialize = "O: GiteratedObject, V: GiteratedOperation<O>"))]
11 pub struct GiteratedMessage<O: GiteratedObject, V: GiteratedOperation<O>> {
12 #[serde(with = "string")]
13 pub object: O,
14 pub operation: String,
15 pub payload: V,
16 }
17
18 mod string {
19 use std::fmt::Display;
20 use std::str::FromStr;
21
22 use serde::{de, Deserialize, Deserializer, Serializer};
23
24 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
25 where
26 T: Display,
27 S: Serializer,
28 {
29 serializer.collect_str(value)
30 }
31
32 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
33 where
34 T: FromStr,
35 T::Err: Display,
36 D: Deserializer<'de>,
37 {
38 String::deserialize(deserializer)?
39 .parse()
40 .map_err(de::Error::custom)
41 }
42 }
43
44 impl GiteratedMessage<AnyObject, AnyOperation> {
45 pub fn try_into<O: GiteratedObject, V: GiteratedOperation<O>>(
46 &self,
47 ) -> Result<GiteratedMessage<O, V>, ()> {
48 let object = O::from_object_str(&self.object.0).map_err(|_| ())?;
49 let payload = serde_json::from_value::<V>(self.payload.0.clone()).map_err(|_| ())?;
50
51 Ok(GiteratedMessage {
52 object,
53 operation: self.operation.clone(),
54 payload,
55 })
56 }
57 }
58
59 impl<V: GiteratedOperation<O> + Debug, O: GiteratedObject + Debug> Debug
60 for GiteratedMessage<O, V>
61 {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("GiteratedMessage")
64 .field("object", &self.object)
65 .field("operation", &self.operation)
66 .field("payload", &self.payload)
67 .finish()
68 }
69 }

giterated-models/src/object.rs

View file
@@ -0,0 +1,87 @@
1 use std::{
2 fmt::{Debug, Display},
3 marker::PhantomData,
4 str::FromStr,
5 };
6
7 use anyhow::Error;
8
9 use crate::{
10 error::{GetValueError, OperationError},
11 object_backend::ObjectBackend,
12 operation::GiteratedOperation,
13 settings::{GetSetting, GetSettingError, SetSetting, SetSettingError, Setting},
14 value::{GetValue, GiteratedObjectValue},
15 };
16
17 mod operations;
18 pub use operations::*;
19
20 #[derive(Debug, Clone)]
21 pub struct Object<'b, O: GiteratedObject, B: ObjectBackend + 'b + Send + Sync + Clone> {
22 pub(crate) inner: O,
23 pub(crate) backend: B,
24 pub(crate) _marker: PhantomData<&'b ()>,
25 }
26
27 impl<'b, B: ObjectBackend + Send + Sync + Clone, O: GiteratedObject> Object<'b, O, B> {
28 pub fn object(&self) -> &O {
29 &self.inner
30 }
31
32 pub unsafe fn new_unchecked(object: O, backend: B) -> Object<'b, O, B> {
33 Object {
34 inner: object,
35 backend,
36 _marker: PhantomData,
37 }
38 }
39 }
40
41 pub trait GiteratedObject: Send + Display + FromStr {
42 fn object_name() -> &'static str;
43
44 fn from_object_str(object_str: &str) -> Result<Self, Error>;
45 }
46
47 impl<'b, O: GiteratedObject + Clone + Debug, B: ObjectBackend> Object<'b, O, B> {
48 pub async fn get<V: GiteratedObjectValue<Object = O> + Send + Debug>(
49 &mut self,
50 ) -> Result<V, OperationError<GetValueError>> {
51 self.request(GetValue {
52 value_name: V::value_name().to_string(),
53 _marker: PhantomData,
54 })
55 .await
56 }
57
58 pub async fn get_setting<S: Setting + Send + Clone + Debug>(
59 &mut self,
60 ) -> Result<S, OperationError<GetSettingError>> {
61 self.request(GetSetting {
62 setting_name: S::name().to_string(),
63 _marker: PhantomData,
64 })
65 .await
66 }
67
68 pub async fn set_setting<S: Setting + Send + Clone + Debug>(
69 &mut self,
70 setting: S,
71 ) -> Result<(), OperationError<SetSettingError>> {
72 self.request(SetSetting {
73 setting_name: S::name().to_string(),
74 value: setting,
75 })
76 .await
77 }
78
79 pub async fn request<R: GiteratedOperation<O> + Debug>(
80 &mut self,
81 request: R,
82 ) -> Result<R::Success, OperationError<R::Failure>> {
83 self.backend
84 .object_operation(self.inner.clone(), request)
85 .await
86 }
87 }

giterated-models/src/object/operations.rs

View file
@@ -0,0 +1,53 @@
1 use std::{convert::Infallible, fmt::Display, str::FromStr};
2
3 use serde::{Deserialize, Serialize};
4
5 use crate::{instance::Instance, operation::GiteratedOperation};
6
7 use super::GiteratedObject;
8
9 #[derive(Debug, Serialize, Deserialize)]
10 pub struct ObjectRequest(pub String);
11
12 #[derive(Serialize, Deserialize)]
13 pub struct ObjectResponse(pub Vec<u8>);
14
15 impl GiteratedOperation<Instance> for ObjectRequest {
16 type Success = ObjectResponse;
17
18 type Failure = ObjectRequestError;
19 }
20
21 #[derive(Debug, thiserror::Error, Serialize, Deserialize)]
22 pub enum ObjectRequestError {
23 #[error("error decoding the object")]
24 Deserialization(String),
25 }
26
27 #[derive(Clone, Debug, Serialize, Deserialize)]
28 #[repr(transparent)]
29 pub struct AnyObject(pub String);
30
31 impl GiteratedObject for AnyObject {
32 fn object_name() -> &'static str {
33 "any"
34 }
35
36 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
37 Ok(Self(object_str.to_string()))
38 }
39 }
40
41 impl Display for AnyObject {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.write_str(&self.0)
44 }
45 }
46
47 impl FromStr for AnyObject {
48 type Err = Infallible;
49
50 fn from_str(s: &str) -> Result<Self, Self::Err> {
51 Ok(Self(s.to_owned()))
52 }
53 }

giterated-models/src/object_backend.rs

View file
@@ -0,0 +1,24 @@
1 use crate::{
2 error::OperationError,
3 object::{GiteratedObject, Object, ObjectRequestError},
4 operation::GiteratedOperation,
5 };
6
7 use std::fmt::Debug;
8
9 #[async_trait::async_trait]
10 pub trait ObjectBackend: Send + Sync + Sized + Clone {
11 async fn object_operation<O, D>(
12 &self,
13 object: O,
14 operation: D,
15 ) -> Result<D::Success, OperationError<D::Failure>>
16 where
17 O: GiteratedObject + Debug,
18 D: GiteratedOperation<O> + Debug;
19
20 async fn get_object<O: GiteratedObject + Debug>(
21 &self,
22 object_str: &str,
23 ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>>;
24 }

giterated-models/src/operation.rs

View file
@@ -0,0 +1,26 @@
1 use std::{any::type_name, fmt::Debug};
2
3 use serde::{de::DeserializeOwned, Deserialize, Serialize};
4 use serde_json::Value;
5
6 use crate::object::GiteratedObject;
7
8 pub trait GiteratedOperation<O: GiteratedObject>: Send + Serialize + DeserializeOwned {
9 type Success: Serialize + DeserializeOwned + Send;
10 type Failure: Serialize + DeserializeOwned + Send;
11
12 fn operation_name() -> &'static str {
13 type_name::<Self>()
14 }
15 }
16
17 #[derive(Clone, Debug, Serialize, Deserialize)]
18 #[serde(transparent)]
19 #[repr(transparent)]
20 pub struct AnyOperation(pub Value);
21
22 impl<O: GiteratedObject> GiteratedOperation<O> for AnyOperation {
23 type Success = Vec<u8>;
24
25 type Failure = Vec<u8>;
26 }

giterated-models/src/repository/mod.rs

View file
@@ -0,0 +1,259 @@
1 use std::fmt::{Display, Formatter};
2 use std::str::FromStr;
3
4 use serde::{Deserialize, Serialize};
5
6 use crate::object::GiteratedObject;
7
8 use super::{instance::Instance, user::User};
9
10 mod operations;
11 mod settings;
12 mod values;
13
14 pub use operations::*;
15 pub use settings::*;
16 pub use values::*;
17
18 /// A repository, defined by the instance it exists on along with
19 /// its owner and name.
20 ///
21 /// # Textual Format
22 /// A repository's textual reference is defined as:
23 ///
24 /// `{owner: User}/{name: String}@{instance: Instance}`
25 ///
26 /// # Examples
27 /// For the repository named `foo` owned by `barson:giterated.dev` on the instance
28 /// `giterated.dev`, the following [`Repository`] initialization would
29 /// be valid:
30 ///
31 /// ```
32 //# use giterated_models::model::repository::Repository;
33 //# use giterated_models::model::instance::Instance;
34 //# use giterated_models::model::user::User;
35 /// let repository = Repository {
36 /// owner: User::from_str("barson:giterated.dev").unwrap(),
37 /// name: String::from("foo"),
38 /// instance: Instance::from_str("giterated.dev").unwrap()
39 /// };
40 ///
41 /// // This is correct
42 /// assert_eq!(Repository::from_str("barson:giterated.dev/[email protected]").unwrap(), repository);
43 /// ```
44 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
45 pub struct Repository {
46 pub owner: User,
47 pub name: String,
48 /// Instance the repository is on
49 pub instance: Instance,
50 }
51
52 impl Display for Repository {
53 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54 f.write_str(&format!("{}/{}@{}", self.owner, self.name, self.instance))
55 }
56 }
57
58 impl GiteratedObject for Repository {
59 fn object_name() -> &'static str {
60 "repository"
61 }
62
63 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
64 Ok(Repository::from_str(object_str)?)
65 }
66 }
67
68 impl TryFrom<String> for Repository {
69 type Error = RepositoryParseError;
70
71 fn try_from(value: String) -> Result<Self, Self::Error> {
72 Self::from_str(&value)
73 }
74 }
75
76 impl FromStr for Repository {
77 type Err = RepositoryParseError;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 let mut by_ampersand = s.split('@');
81 let mut path_split = by_ampersand.next().unwrap().split('/');
82
83 let instance = Instance::from_str(by_ampersand.next().unwrap()).unwrap();
84 let owner = User::from_str(path_split.next().unwrap()).unwrap();
85 let name = path_split.next().unwrap().to_string();
86
87 Ok(Self {
88 instance,
89 owner,
90 name,
91 })
92 }
93 }
94
95 #[derive(Debug, thiserror::Error)]
96 pub enum RepositoryParseError {}
97
98 /// Visibility of the repository to the general eye
99 #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)]
100 #[sqlx(type_name = "visibility", rename_all = "lowercase")]
101 pub enum RepositoryVisibility {
102 Public,
103 Unlisted,
104 Private,
105 }
106
107 /// Implements [`Display`] for [`RepositoryVisiblity`] using [`Debug`]
108 impl Display for RepositoryVisibility {
109 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{:?}", self)
111 }
112 }
113
114 #[derive(Clone, Debug, Serialize, Deserialize)]
115 pub struct RepositoryView {
116 /// Name of the repository
117 ///
118 /// This is different than the [`Repository`] name,
119 /// which may be a path.
120 pub name: String,
121 /// Owner of the Repository
122 pub owner: User,
123 /// Repository description
124 pub description: Option<String>,
125 /// Repository visibility
126 pub visibility: RepositoryVisibility,
127 /// Default branch of the repository
128 pub default_branch: String,
129 /// Last commit made to the repository
130 pub latest_commit: Option<Commit>,
131 /// Revision of the displayed tree
132 pub tree_rev: Option<String>,
133 /// Repository tree
134 pub tree: Vec<RepositoryTreeEntry>,
135 }
136
137 #[derive(Debug, Clone, Serialize, Deserialize)]
138 pub enum RepositoryObjectType {
139 Tree,
140 Blob,
141 }
142
143 /// Stored info for our tree entries
144 #[derive(Debug, Clone, Serialize, Deserialize)]
145 pub struct RepositoryTreeEntry {
146 /// Name of the tree/blob
147 pub name: String,
148 /// Type of the tree entry
149 pub object_type: RepositoryObjectType,
150 /// Git supplies us with the mode at all times, and people like it displayed.
151 pub mode: i32,
152 /// File size
153 pub size: Option<usize>,
154 /// Last commit made to the tree/blob
155 pub last_commit: Option<Commit>,
156 }
157
158 impl RepositoryTreeEntry {
159 // I love you Emilia <3
160 pub fn new(name: &str, object_type: RepositoryObjectType, mode: i32) -> Self {
161 Self {
162 name: name.to_string(),
163 object_type,
164 mode,
165 size: None,
166 last_commit: None,
167 }
168 }
169 }
170
171 #[derive(Debug, Clone, Serialize, Deserialize)]
172 pub struct RepositoryTreeEntryWithCommit {
173 pub tree_entry: RepositoryTreeEntry,
174 pub commit: Commit,
175 }
176
177 /// Info about a git commit
178 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
179 pub struct Commit {
180 /// Unique commit ID
181 pub oid: String,
182 /// Shortened abbreviated OID
183 /// This starts at the git config's "core.abbrev" length (default 7 characters) and
184 /// iteratively extends to a longer string if that length is ambiguous. The
185 /// result will be unambiguous (at least until new objects are added to the repository).
186 pub short_oid: String,
187 /// Full commit message
188 pub message: Option<String>,
189 /// Who created the commit
190 pub author: CommitSignature,
191 /// Who committed the commit
192 pub committer: CommitSignature,
193 /// Time when the commit happened
194 pub time: chrono::NaiveDateTime,
195 }
196
197 /// Gets all info from [`git2::Commit`] for easy use
198 impl From<git2::Commit<'_>> for Commit {
199 fn from(commit: git2::Commit<'_>) -> Self {
200 Self {
201 oid: commit.id().to_string(),
202 // This shouldn't ever fail, as we already know the object has an oid.
203 short_oid: commit
204 .as_object()
205 .short_id()
206 .unwrap()
207 .as_str()
208 .unwrap()
209 .to_string(),
210 message: commit.message().map(|message| message.to_string()),
211 author: commit.author().into(),
212 committer: commit.committer().into(),
213 time: chrono::NaiveDateTime::from_timestamp_opt(commit.time().seconds(), 0).unwrap(),
214 }
215 }
216 }
217
218 /// Git commit signature
219 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
220 pub struct CommitSignature {
221 pub name: Option<String>,
222 pub email: Option<String>,
223 pub time: chrono::NaiveDateTime,
224 }
225
226 /// Converts the signature from git2 into something usable without explicit lifetimes.
227 impl From<git2::Signature<'_>> for CommitSignature {
228 fn from(signature: git2::Signature<'_>) -> Self {
229 Self {
230 name: signature.name().map(|name| name.to_string()),
231 email: signature.email().map(|email| email.to_string()),
232 time: chrono::NaiveDateTime::from_timestamp_opt(signature.when().seconds(), 0).unwrap(),
233 }
234 }
235 }
236
237 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
238 pub struct RepositorySummary {
239 pub repository: Repository,
240 pub owner: User,
241 pub visibility: RepositoryVisibility,
242 pub description: Option<String>,
243 pub last_commit: Option<Commit>,
244 }
245
246 #[derive(Clone, Debug, Serialize, Deserialize)]
247 pub struct IssueLabel {
248 pub name: String,
249 pub color: String,
250 }
251
252 #[derive(Clone, Debug, Serialize, Deserialize)]
253 pub struct RepositoryIssue {
254 pub author: User,
255 pub id: u64,
256 pub title: String,
257 pub contents: String,
258 pub labels: Vec<IssueLabel>,
259 }

giterated-models/src/repository/operations.rs

View file
@@ -0,0 +1,111 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{
4 error::{OperationError, RepositoryError},
5 object::Object,
6 object_backend::ObjectBackend,
7 operation::GiteratedOperation,
8 };
9
10 use super::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry};
11
12 /// A request to get a repository's information.
13 ///
14 /// # Authentication
15 /// - Instance Authentication
16 /// - Validate request against the `issued_for` public key
17 /// - Validate User token against the user's instance's public key
18 /// # Authorization
19 /// - User Authorization
20 /// - Potential User permissions checks
21 #[derive(Clone, Debug, Serialize, Deserialize)]
22 pub struct RepositoryIssuesCountRequest;
23
24 impl GiteratedOperation<Repository> for RepositoryIssuesCountRequest {
25 type Success = u64;
26 type Failure = RepositoryError;
27 }
28
29 /// A request to get a repository's issues count.
30 ///
31 /// # Authentication
32 /// - Instance Authentication
33 /// - Validate request against the `issued_for` public key
34 /// - Validate User token against the user's instance's public key
35 /// # Authorization
36 /// - User Authorization
37 /// - Potential User permissions checks
38 #[derive(Clone, Debug, Serialize, Deserialize)]
39 pub struct RepositoryIssueLabelsRequest;
40
41 impl GiteratedOperation<Repository> for RepositoryIssueLabelsRequest {
42 type Success = Vec<IssueLabel>;
43 type Failure = RepositoryError;
44 }
45
46 /// A request to get a repository's issue labels.
47 ///
48 /// # Authentication
49 /// - Instance Authentication
50 /// - Validate request against the `issued_for` public key
51 /// - Validate User token against the user's instance's public key
52 /// # Authorization
53 /// - User Authorization
54 /// - Potential User permissions checks
55 #[derive(Clone, Debug, Serialize, Deserialize)]
56 pub struct RepositoryIssuesRequest;
57
58 impl GiteratedOperation<Repository> for RepositoryIssuesRequest {
59 type Success = Vec<RepositoryIssue>;
60 type Failure = RepositoryError;
61 }
62
63 /// A request to inspect the tree of a repository.
64 ///
65 /// # Authentication
66 /// - Instance Authentication
67 /// - Validate request against the `issued_for` public key
68 /// - Validate User token against the user's instance's public key
69 /// # Authorization
70 /// - User Authorization
71 /// - Potential User permissions checks
72 #[derive(Clone, Debug, Serialize, Deserialize)]
73 pub struct RepositoryFileInspectRequest {
74 pub path: RepositoryTreeEntry,
75 }
76
77 impl GiteratedOperation<Repository> for RepositoryFileInspectRequest {
78 type Success = Vec<RepositoryTreeEntry>;
79 type Failure = RepositoryError;
80 }
81
82 impl<B: ObjectBackend + std::fmt::Debug> Object<'_, Repository, B> {
83 pub async fn issues_count(&mut self) -> Result<u64, OperationError<RepositoryError>> {
84 self.request::<RepositoryIssuesCountRequest>(RepositoryIssuesCountRequest)
85 .await
86 }
87
88 pub async fn issue_labels(
89 &mut self,
90 ) -> Result<Vec<IssueLabel>, OperationError<RepositoryError>> {
91 self.request::<RepositoryIssueLabelsRequest>(RepositoryIssueLabelsRequest)
92 .await
93 }
94
95 pub async fn issues(
96 &mut self,
97 ) -> Result<Vec<RepositoryIssue>, OperationError<RepositoryError>> {
98 self.request::<RepositoryIssuesRequest>(RepositoryIssuesRequest)
99 .await
100 }
101
102 pub async fn inspect_files(
103 &mut self,
104 entry: &RepositoryTreeEntry,
105 ) -> Result<Vec<RepositoryTreeEntry>, OperationError<RepositoryError>> {
106 self.request::<RepositoryFileInspectRequest>(RepositoryFileInspectRequest {
107 path: entry.clone(),
108 })
109 .await
110 }
111 }

giterated-models/src/repository/settings.rs

View file
@@ -0,0 +1,21 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::settings::Setting;
4
5 #[derive(Debug, Serialize, Deserialize)]
6 pub struct RepositoryDescription(pub String);
7
8 impl Setting for RepositoryDescription {
9 fn name() -> &'static str {
10 "Repository Description"
11 }
12 }
13
14 #[derive(Debug, Serialize, Deserialize)]
15 pub struct RepositoryVisibilitySetting(pub String);
16
17 impl Setting for RepositoryVisibilitySetting {
18 fn name() -> &'static str {
19 "Repository Visibility"
20 }
21 }

giterated-models/src/repository/values.rs

View file
@@ -0,0 +1,45 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{settings::Setting, value::GiteratedObjectValue};
4
5 use super::{Repository, RepositoryVisibility};
6
7 // pub struct RepositorySetting<V: GiteratedObjectValue>(pub V);
8
9 // impl<O: GiteratedObject, V: GiteratedObjectValue<Object = O> + Send> GiteratedOperation<O>
10 // for RepositorySetting<V>
11 // {
12 // fn operation_name(&self) -> &'static str {
13 // "setting_get"
14 // }
15 // type Success = V;
16 // type Failure = GetValueError;
17 // }
18
19 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
20 pub struct Description(pub String);
21
22 impl GiteratedObjectValue for Description {
23 type Object = Repository;
24
25 fn value_name() -> &'static str {
26 "description"
27 }
28 }
29
30 impl Setting for Description {
31 fn name() -> &'static str {
32 "description"
33 }
34 }
35
36 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
37 pub struct Visibility(pub RepositoryVisibility);
38
39 impl GiteratedObjectValue for Visibility {
40 type Object = Repository;
41
42 fn value_name() -> &'static str {
43 "visibility"
44 }
45 }

giterated-models/src/settings/mod.rs

View file
@@ -0,0 +1,18 @@
1 mod operations;
2
3 pub use operations::*;
4 use serde::{de::DeserializeOwned, Deserialize, Serialize};
5 use serde_json::Value;
6
7 pub trait Setting: Serialize + DeserializeOwned {
8 fn name() -> &'static str;
9 }
10
11 #[derive(Debug, Clone, Serialize, Deserialize)]
12 pub struct AnySetting(pub Value);
13
14 impl Setting for AnySetting {
15 fn name() -> &'static str {
16 "any"
17 }
18 }

giterated-models/src/settings/operations.rs

View file
@@ -0,0 +1,48 @@
1 use std::{fmt::Debug, marker::PhantomData};
2
3 use serde::{de::DeserializeOwned, Deserialize, Serialize};
4 use thiserror::Error;
5
6 use crate::{object::GiteratedObject, operation::GiteratedOperation};
7
8 use super::Setting;
9
10 #[derive(Serialize, Deserialize, Debug, Clone)]
11 pub struct GetSetting<S: Setting + std::fmt::Debug + Clone> {
12 pub setting_name: String,
13 pub _marker: PhantomData<S>,
14 }
15
16 impl<O: GiteratedObject, S: Setting + Send + DeserializeOwned + Debug + Clone> GiteratedOperation<O>
17 for GetSetting<S>
18 {
19 fn operation_name() -> &'static str {
20 "get_setting"
21 }
22
23 type Success = S;
24
25 type Failure = GetSettingError;
26 }
27
28 #[derive(Error, Debug, Serialize, Deserialize)]
29 pub enum GetSettingError {}
30 #[derive(Serialize, Deserialize, Debug, Clone)]
31 #[serde(bound(deserialize = "S: Setting"))]
32 pub struct SetSetting<S: Setting> {
33 pub setting_name: String,
34 pub value: S,
35 }
36
37 impl<O: GiteratedObject, S: Setting + Send> GiteratedOperation<O> for SetSetting<S> {
38 fn operation_name() -> &'static str {
39 "set_setting"
40 }
41
42 type Success = ();
43
44 type Failure = SetSettingError;
45 }
46
47 #[derive(Error, Debug, Serialize, Deserialize)]
48 pub enum SetSettingError {}

giterated-models/src/user/mod.rs

View file
@@ -0,0 +1,97 @@
1 mod operations;
2 mod settings;
3 mod values;
4
5 use std::{
6 fmt::{Display, Formatter},
7 str::FromStr,
8 };
9
10 pub use operations::*;
11 use secrecy::{CloneableSecret, DebugSecret, SerializableSecret, Zeroize};
12 use serde::{Deserialize, Serialize};
13 pub use settings::*;
14 pub use values::*;
15
16 use crate::{instance::Instance, object::GiteratedObject};
17
18 /// A user, defined by its username and instance.
19 ///
20 /// # Textual Format
21 /// A user's textual reference is defined as:
22 ///
23 /// `{username: String}:{instance: Instance}`
24 ///
25 /// # Examples
26 /// For the user with the username `barson` and the instance `giterated.dev`,
27 /// the following [`User`] initialization would be valid:
28 ///
29 /// ```
30 /// let user = User {
31 /// username: String::from("barson"),
32 /// instance: Instance::from_str("giterated.dev").unwrap()
33 /// };
34 ///
35 /// // This is correct
36 /// assert_eq!(User::from_str("barson:giterated.dev").unwrap(), user);
37 /// ```
38 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
39 pub struct User {
40 pub username: String,
41 pub instance: Instance,
42 }
43
44 impl GiteratedObject for User {
45 fn object_name() -> &'static str {
46 "user"
47 }
48
49 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
50 Ok(User::from_str(object_str).unwrap())
51 }
52 }
53
54 impl Display for User {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 write!(f, "{}:{}", self.username, self.instance.url)
57 }
58 }
59
60 impl From<String> for User {
61 fn from(user_string: String) -> Self {
62 User::from_str(&user_string).unwrap()
63 }
64 }
65
66 impl FromStr for User {
67 type Err = UserParseError;
68
69 fn from_str(s: &str) -> Result<Self, Self::Err> {
70 if s.contains('/') {
71 return Err(UserParseError);
72 }
73
74 let mut colon_split = s.split(':');
75 let username = colon_split.next().unwrap().to_string();
76 let instance = Instance::from_str(colon_split.next().unwrap()).unwrap();
77
78 Ok(Self { username, instance })
79 }
80 }
81
82 #[derive(thiserror::Error, Debug)]
83 #[error("failed to parse user")]
84 pub struct UserParseError;
85
86 #[derive(Clone, Debug, Serialize, Deserialize)]
87 pub struct Password(pub String);
88
89 impl Zeroize for Password {
90 fn zeroize(&mut self) {
91 self.0.zeroize()
92 }
93 }
94
95 impl SerializableSecret for Password {}
96 impl CloneableSecret for Password {}
97 impl DebugSecret for Password {}

giterated-models/src/user/operations.rs

View file
@@ -0,0 +1,36 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{
4 error::{OperationError, UserError},
5 instance::Instance,
6 object::Object,
7 object_backend::ObjectBackend,
8 operation::GiteratedOperation,
9 repository::Repository,
10 };
11
12 use super::User;
13
14 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15 pub struct UserRepositoriesRequest {
16 pub instance: Instance,
17 pub user: User,
18 }
19
20 impl GiteratedOperation<User> for UserRepositoriesRequest {
21 type Success = Vec<Repository>;
22 type Failure = UserError;
23 }
24
25 impl<B: ObjectBackend + std::fmt::Debug> Object<'_, User, B> {
26 pub async fn repositories(
27 &mut self,
28 instance: &Instance,
29 ) -> Result<Vec<Repository>, OperationError<UserError>> {
30 self.request::<UserRepositoriesRequest>(UserRepositoriesRequest {
31 instance: instance.clone(),
32 user: self.inner.clone(),
33 })
34 .await
35 }
36 }

giterated-models/src/user/settings.rs

View file
@@ -0,0 +1,30 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::settings::Setting;
4
5 #[derive(Debug, Serialize, Deserialize)]
6 pub struct UserBio(pub String);
7
8 impl Setting for UserBio {
9 fn name() -> &'static str {
10 "Bio"
11 }
12 }
13
14 #[derive(Debug, Serialize, Deserialize, Clone)]
15 pub struct UserDisplayName(pub String);
16
17 impl Setting for UserDisplayName {
18 fn name() -> &'static str {
19 "Display Name"
20 }
21 }
22
23 #[derive(Debug, Serialize, Deserialize)]
24 pub struct UserDisplayImage(pub String);
25
26 impl Setting for UserDisplayImage {
27 fn name() -> &'static str {
28 "Profile Image"
29 }
30 }

giterated-models/src/user/values.rs

View file
@@ -0,0 +1,27 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::value::GiteratedObjectValue;
4
5 use super::User;
6
7 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
8 pub struct Bio(pub String);
9
10 impl GiteratedObjectValue for Bio {
11 type Object = User;
12
13 fn value_name() -> &'static str {
14 "bio"
15 }
16 }
17
18 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
19 pub struct DisplayName(pub String);
20
21 impl GiteratedObjectValue for DisplayName {
22 type Object = User;
23
24 fn value_name() -> &'static str {
25 "display_name"
26 }
27 }

giterated-models/src/value.rs

View file
@@ -0,0 +1,53 @@
1 use std::{fmt::Debug, marker::PhantomData};
2
3 use serde::{de::DeserializeOwned, Deserialize, Serialize};
4 use serde_json::Value;
5
6 use crate::{error::GetValueError, object::GiteratedObject, operation::GiteratedOperation};
7
8 pub trait GiteratedObjectValue: Serialize + DeserializeOwned {
9 type Object: GiteratedObject;
10
11 fn value_name() -> &'static str;
12 }
13
14 #[derive(Serialize, Deserialize, Debug, Clone)]
15 pub struct GetValue<V: GiteratedObjectValue> {
16 pub value_name: String,
17 pub(crate) _marker: PhantomData<V>,
18 }
19
20 impl<O: GiteratedObject + Send, V: GiteratedObjectValue<Object = O> + Send> GiteratedOperation<O>
21 for GetValue<V>
22 {
23 fn operation_name() -> &'static str {
24 "get_value"
25 }
26 type Success = V;
27 type Failure = GetValueError;
28 }
29
30 #[derive(Debug, Clone, Deserialize, Serialize)]
31 #[serde(transparent)]
32 pub struct AnyValue<O> {
33 value: Value,
34 #[serde(skip)]
35 _marker: PhantomData<O>,
36 }
37
38 impl<O: GiteratedObject> AnyValue<O> {
39 pub unsafe fn from_raw(value: Value) -> Self {
40 Self {
41 value,
42 _marker: Default::default(),
43 }
44 }
45 }
46
47 impl<O: GiteratedObject> GiteratedObjectValue for AnyValue<O> {
48 type Object = O;
49
50 fn value_name() -> &'static str {
51 todo!()
52 }
53 }