Major post-refactor cleanup
parent: tbd commit: f90d7fb
Showing 21 changed files with 165 insertions and 1039 deletions
src/authentication.rs
@@ -6,13 +6,7 @@ use tokio::{fs::File, io::AsyncReadExt}; | ||
6 | 6 | use toml::Table; |
7 | 7 | |
8 | 8 | use crate::{ |
9 | messages::{ | |
10 | authentication::{ | |
11 | AuthenticationTokenRequest, AuthenticationTokenResponse, TokenExtensionRequest, | |
12 | TokenExtensionResponse, | |
13 | }, | |
14 | InstanceAuthenticated, | |
15 | }, | |
9 | messages::authentication::{AuthenticationTokenResponse, TokenExtensionResponse}, | |
16 | 10 | model::{authenticated::UserAuthenticationToken, instance::Instance, user::User}, |
17 | 11 | }; |
18 | 12 | |
@@ -76,7 +70,7 @@ impl AuthenticationTokenGranter { | ||
76 | 70 | &mut self, |
77 | 71 | issued_for: impl ToOwned<Owned = Instance>, |
78 | 72 | username: String, |
79 | password: String, | |
73 | _password: String, | |
80 | 74 | ) -> Result<AuthenticationTokenResponse, Error> { |
81 | 75 | let private_key = { |
82 | 76 | let mut file = File::open( |
src/backend/discovery.rs
@@ -1,21 +1,9 @@ | ||
1 | use std::{ | |
2 | collections::hash_map::DefaultHasher, | |
3 | hash::{Hash, Hasher}, | |
4 | }; | |
1 | use std::hash::Hash; | |
5 | 2 | |
6 | use anyhow::Error; | |
7 | use base64::{engine::general_purpose::STANDARD, Engine as _}; | |
8 | 3 | use chrono::{DateTime, Utc}; |
9 | 4 | use serde::{Deserialize, Serialize}; |
10 | 5 | use sqlx::PgPool; |
11 | 6 | |
12 | use crate::{ | |
13 | messages::discovery::{DiscoveryMessage, DiscoveryMessageKind}, | |
14 | model::{discovery::DiscoveryItem, repository::Repository}, | |
15 | }; | |
16 | ||
17 | use super::DiscoveryBackend; | |
18 | ||
19 | 7 | pub struct GiteratedDiscoveryProtocol { |
20 | 8 | pub pool: PgPool, |
21 | 9 | } |
@@ -27,78 +15,6 @@ pub enum DiscoveryType { | ||
27 | 15 | Repository, |
28 | 16 | } |
29 | 17 | |
30 | #[async_trait::async_trait] | |
31 | impl DiscoveryBackend for GiteratedDiscoveryProtocol { | |
32 | async fn try_handle(&mut self, request: &DiscoveryMessage) -> Result<bool, Error> { | |
33 | if request | |
34 | .message | |
35 | .validate(&request.message.instance) | |
36 | .await | |
37 | .is_err() | |
38 | { | |
39 | return Ok(false); | |
40 | } | |
41 | ||
42 | let inner = request.message.inner().await.clone(); | |
43 | ||
44 | match inner { | |
45 | DiscoveryMessageKind::Discoveries(mut discoveries) => { | |
46 | let discoveries = discoveries.discoveries.drain(..).map(|discovery| { | |
47 | let hash = { | |
48 | let mut hasher = DefaultHasher::new(); | |
49 | discovery.hash(&mut hasher); | |
50 | STANDARD.encode(hasher.finish().to_be_bytes()) | |
51 | }; | |
52 | ||
53 | let (discovery_type, discovery) = match discovery { | |
54 | DiscoveryItem::Instance { | |
55 | instance, | |
56 | signature, | |
57 | } => (DiscoveryType::Instance, instance.to_string()), | |
58 | DiscoveryItem::Repository { | |
59 | repository, | |
60 | signature, | |
61 | } => (DiscoveryType::Repository, repository.to_string()), | |
62 | }; | |
63 | ||
64 | DiscoveriesRow { | |
65 | discovery_hash: hash, | |
66 | discovery_time: Utc::now(), | |
67 | discovery_type, | |
68 | discovery, | |
69 | } | |
70 | }); | |
71 | ||
72 | for row in discoveries { | |
73 | let result = sqlx::query!( | |
74 | r#"INSERT INTO discoveries VALUES ($1, $2, $3, $4)"#, | |
75 | row.discovery_hash, | |
76 | row.discovery_time.to_string(), | |
77 | row.discovery_type as _, | |
78 | row.discovery | |
79 | ) | |
80 | .execute(&self.pool) | |
81 | .await; | |
82 | ||
83 | match result { | |
84 | Ok(_) => {} | |
85 | Err(err) => { | |
86 | error!("Error inserting discovery. {:?}", err); | |
87 | } | |
88 | } | |
89 | } | |
90 | Ok(true) | |
91 | } | |
92 | DiscoveryMessageKind::Offer(offer) => Ok(true), | |
93 | DiscoveryMessageKind::Request(request) => Ok(true), | |
94 | } | |
95 | } | |
96 | ||
97 | async fn search(&mut self, _search: &str) -> Result<Vec<Repository>, Error> { | |
98 | todo!() | |
99 | } | |
100 | } | |
101 | ||
102 | 18 | #[derive(Debug, sqlx::FromRow, sqlx::Type)] |
103 | 19 | pub struct DiscoveriesRow { |
104 | 20 | discovery_hash: String, |
src/backend/git.rs
@@ -6,8 +6,6 @@ use sqlx::{Either, PgPool}; | ||
6 | 6 | use std::path::{Path, PathBuf}; |
7 | 7 | use thiserror::Error; |
8 | 8 | |
9 | use crate::messages::ValidatedUserAuthenticated; | |
10 | ||
11 | 9 | use crate::model::instance::Instance; |
12 | 10 | use crate::model::repository::{ |
13 | 11 | Commit, Repository, RepositoryObjectType, RepositorySummary, RepositoryTreeEntry, |
@@ -212,7 +210,7 @@ impl GitBackend { | ||
212 | 210 | impl RepositoryBackend for GitBackend { |
213 | 211 | async fn create_repository( |
214 | 212 | &mut self, |
215 | user: &User, | |
213 | _user: &User, | |
216 | 214 | request: &CreateRepositoryRequest, |
217 | 215 | ) -> Result<CreateRepositoryResponse, Error> { |
218 | 216 | // Check if repository already exists in the database |
@@ -439,7 +437,7 @@ impl RepositoryBackend for GitBackend { | ||
439 | 437 | |
440 | 438 | async fn repository_file_inspect( |
441 | 439 | &mut self, |
442 | requester: Option<&User>, | |
440 | _requester: Option<&User>, | |
443 | 441 | _request: &RepositoryFileInspectRequest, |
444 | 442 | ) -> Result<RepositoryFileInspectionResponse, Error> { |
445 | 443 | todo!() |
@@ -480,21 +478,24 @@ impl RepositoryBackend for GitBackend { | ||
480 | 478 | impl IssuesBackend for GitBackend { |
481 | 479 | fn issues_count( |
482 | 480 | &mut self, |
483 | _request: &ValidatedUserAuthenticated<RepositoryIssuesCountRequest>, | |
481 | _requester: Option<&User>, | |
482 | _request: &RepositoryIssuesCountRequest, | |
484 | 483 | ) -> Result<RepositoryIssuesCountResponse, Error> { |
485 | 484 | todo!() |
486 | 485 | } |
487 | 486 | |
488 | 487 | fn issue_labels( |
489 | 488 | &mut self, |
490 | _request: &ValidatedUserAuthenticated<RepositoryIssueLabelsRequest>, | |
489 | _requester: Option<&User>, | |
490 | _request: &RepositoryIssueLabelsRequest, | |
491 | 491 | ) -> Result<RepositoryIssueLabelsResponse, Error> { |
492 | 492 | todo!() |
493 | 493 | } |
494 | 494 | |
495 | 495 | fn issues( |
496 | 496 | &mut self, |
497 | _request: &ValidatedUserAuthenticated<RepositoryIssuesRequest>, | |
497 | _requester: Option<&User>, | |
498 | _request: &RepositoryIssuesRequest, | |
498 | 499 | ) -> Result<RepositoryIssuesResponse, Error> { |
499 | 500 | todo!() |
500 | 501 | } |
src/backend/mod.rs
@@ -12,7 +12,6 @@ use crate::{ | ||
12 | 12 | AuthenticationTokenRequest, AuthenticationTokenResponse, RegisterAccountRequest, |
13 | 13 | RegisterAccountResponse, |
14 | 14 | }, |
15 | discovery::DiscoveryMessage, | |
16 | 15 | repository::{ |
17 | 16 | CreateRepositoryRequest, CreateRepositoryResponse, RepositoryFileInspectRequest, |
18 | 17 | RepositoryFileInspectionResponse, RepositoryInfoRequest, RepositoryIssueLabelsRequest, |
@@ -23,10 +22,9 @@ use crate::{ | ||
23 | 22 | UserBioRequest, UserBioResponse, UserDisplayImageRequest, UserDisplayImageResponse, |
24 | 23 | UserDisplayNameRequest, UserDisplayNameResponse, |
25 | 24 | }, |
26 | ValidatedUserAuthenticated, | |
27 | 25 | }, |
28 | 26 | model::{ |
29 | repository::{Repository, RepositorySummary, RepositoryView}, | |
27 | repository::{RepositorySummary, RepositoryView}, | |
30 | 28 | user::User, |
31 | 29 | }, |
32 | 30 | }; |
@@ -55,15 +53,18 @@ pub trait RepositoryBackend: IssuesBackend { | ||
55 | 53 | pub trait IssuesBackend { |
56 | 54 | fn issues_count( |
57 | 55 | &mut self, |
58 | request: &ValidatedUserAuthenticated<RepositoryIssuesCountRequest>, | |
56 | requester: Option<&User>, | |
57 | request: &RepositoryIssuesCountRequest, | |
59 | 58 | ) -> Result<RepositoryIssuesCountResponse, Error>; |
60 | 59 | fn issue_labels( |
61 | 60 | &mut self, |
62 | request: &ValidatedUserAuthenticated<RepositoryIssueLabelsRequest>, | |
61 | requester: Option<&User>, | |
62 | request: &RepositoryIssueLabelsRequest, | |
63 | 63 | ) -> Result<RepositoryIssueLabelsResponse, Error>; |
64 | 64 | fn issues( |
65 | 65 | &mut self, |
66 | request: &ValidatedUserAuthenticated<RepositoryIssuesRequest>, | |
66 | requester: Option<&User>, | |
67 | request: &RepositoryIssuesRequest, | |
67 | 68 | ) -> Result<RepositoryIssuesResponse, Error>; |
68 | 69 | } |
69 | 70 | |
@@ -95,9 +96,3 @@ pub trait UserBackend: AuthBackend { | ||
95 | 96 | async fn bio(&mut self, request: UserBioRequest) -> Result<UserBioResponse, Error>; |
96 | 97 | async fn exists(&mut self, user: &User) -> Result<bool, Error>; |
97 | 98 | } |
98 | ||
99 | #[async_trait::async_trait] | |
100 | pub trait DiscoveryBackend { | |
101 | async fn try_handle(&mut self, request: &DiscoveryMessage) -> Result<bool, Error>; | |
102 | async fn search(&mut self, search: &str) -> Result<Vec<Repository>, Error>; | |
103 | } |
src/backend/user.rs
@@ -203,6 +203,7 @@ impl AuthBackend for UserAuth { | ||
203 | 203 | } |
204 | 204 | } |
205 | 205 | |
206 | #[allow(unused)] | |
206 | 207 | #[derive(Debug, sqlx::FromRow)] |
207 | 208 | struct UserRow { |
208 | 209 | pub username: String, |
src/connection.rs
@@ -4,47 +4,16 @@ pub mod repository; | ||
4 | 4 | pub mod user; |
5 | 5 | pub mod wrapper; |
6 | 6 | |
7 | use std::{any::type_name, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; | |
7 | use std::{any::type_name, collections::HashMap}; | |
8 | 8 | |
9 | 9 | use anyhow::Error; |
10 | use futures_util::{stream::StreamExt, SinkExt}; | |
11 | use semver::Version; | |
12 | 10 | use serde::{de::DeserializeOwned, Serialize}; |
13 | use tokio::{ | |
14 | net::TcpStream, | |
15 | sync::{ | |
16 | broadcast::{Receiver, Sender}, | |
17 | Mutex, | |
18 | }, | |
19 | task::JoinHandle, | |
20 | }; | |
21 | use tokio_tungstenite::{tungstenite::Message, WebSocketStream}; | |
11 | use tokio::{net::TcpStream, task::JoinHandle}; | |
12 | use tokio_tungstenite::WebSocketStream; | |
22 | 13 | |
23 | 14 | use crate::{ |
24 | authentication::AuthenticationTokenGranter, | |
25 | backend::{DiscoveryBackend, RepositoryBackend, UserBackend}, | |
26 | handshake::{HandshakeFinalize, HandshakeMessage, HandshakeResponse}, | |
27 | listener::Listeners, | |
28 | messages::{ | |
29 | authentication::{ | |
30 | AuthenticationMessage, AuthenticationRequest, AuthenticationResponse, | |
31 | TokenExtensionResponse, | |
32 | }, | |
33 | repository::{ | |
34 | RepositoryMessage, RepositoryMessageKind, RepositoryRequest, RepositoryResponse, | |
35 | }, | |
36 | user::{ | |
37 | UserMessage, UserMessageKind, UserMessageRequest, UserMessageResponse, | |
38 | UserRepositoriesResponse, | |
39 | }, | |
40 | ErrorMessage, MessageKind, | |
41 | }, | |
42 | model::{ | |
43 | instance::{Instance, InstanceMeta}, | |
44 | repository::Repository, | |
45 | user::User, | |
46 | }, | |
47 | validate_version, version, | |
15 | messages::ErrorMessage, | |
16 | model::instance::{Instance, InstanceMeta}, | |
48 | 17 | }; |
49 | 18 | |
50 | 19 | #[derive(Debug, thiserror::Error)] |
@@ -63,7 +32,6 @@ pub struct RawConnection { | ||
63 | 32 | |
64 | 33 | pub struct InstanceConnection { |
65 | 34 | pub instance: InstanceMeta, |
66 | pub sender: Sender<MessageKind>, | |
67 | 35 | pub task: JoinHandle<()>, |
68 | 36 | } |
69 | 37 | |
@@ -78,212 +46,6 @@ pub struct Connections { | ||
78 | 46 | pub instance_connections: HashMap<Instance, InstanceConnection>, |
79 | 47 | } |
80 | 48 | |
81 | pub async fn connection_worker( | |
82 | mut socket: &mut WebSocketStream<TcpStream>, | |
83 | handshaked: &mut bool, | |
84 | listeners: &Arc<Mutex<Listeners>>, | |
85 | connections: &Arc<Mutex<Connections>>, | |
86 | backend: &Arc<Mutex<dyn RepositoryBackend + Send>>, | |
87 | user_backend: &Arc<Mutex<dyn UserBackend + Send>>, | |
88 | auth_granter: &Arc<Mutex<AuthenticationTokenGranter>>, | |
89 | discovery_backend: &Arc<Mutex<dyn DiscoveryBackend + Send>>, | |
90 | addr: &SocketAddr, | |
91 | ) -> Result<(), ConnectionError> { | |
92 | let this_instance = Instance { | |
93 | url: String::from("giterated.dev"), | |
94 | }; | |
95 | ||
96 | let message = socket | |
97 | .next() | |
98 | .await | |
99 | .ok_or_else(|| ConnectionError::Shutdown)? | |
100 | .map_err(|e| Error::from(e))?; | |
101 | ||
102 | let payload = match message { | |
103 | Message::Text(text) => text.into_bytes(), | |
104 | Message::Binary(bytes) => bytes, | |
105 | Message::Ping(_) => return Ok(()), | |
106 | Message::Pong(_) => return Ok(()), | |
107 | Message::Close(_) => { | |
108 | info!("Closing connection with {}.", addr); | |
109 | ||
110 | return Err(ConnectionError::Shutdown); | |
111 | } | |
112 | _ => unreachable!(), | |
113 | }; | |
114 | ||
115 | let message = serde_json::from_slice::<MessageKind>(&payload).map_err(|e| Error::from(e))?; | |
116 | ||
117 | if let MessageKind::Handshake(handshake) = message { | |
118 | match handshake { | |
119 | HandshakeMessage::Initiate(request) => { | |
120 | unimplemented!() | |
121 | } | |
122 | HandshakeMessage::Response(response) => { | |
123 | unimplemented!() | |
124 | } | |
125 | HandshakeMessage::Finalize(response) => { | |
126 | unimplemented!() | |
127 | } | |
128 | } | |
129 | } | |
130 | ||
131 | if !*handshaked { | |
132 | return Ok(()); | |
133 | } | |
134 | ||
135 | if let MessageKind::Repository(repository) = &message { | |
136 | if repository.target.instance != this_instance { | |
137 | info!("Forwarding command to {}", repository.target.instance.url); | |
138 | // We need to send this command to a different instance | |
139 | ||
140 | let mut listener = send_and_get_listener(message, &listeners, &connections).await; | |
141 | ||
142 | // Wait for response | |
143 | while let Ok(message) = listener.recv().await { | |
144 | if let MessageKind::Repository(RepositoryMessage { | |
145 | command: RepositoryMessageKind::Response(_), | |
146 | .. | |
147 | }) = message | |
148 | { | |
149 | let _result = send(&mut socket, message).await; | |
150 | } | |
151 | } | |
152 | ||
153 | return Ok(()); | |
154 | } else { | |
155 | // This message is targeting this instance | |
156 | match &repository.command { | |
157 | RepositoryMessageKind::Request(request) => match request.clone() { | |
158 | RepositoryRequest::CreateRepository(request) => { | |
159 | unimplemented!(); | |
160 | } | |
161 | RepositoryRequest::RepositoryFileInspect(request) => { | |
162 | unimplemented!() | |
163 | } | |
164 | RepositoryRequest::RepositoryInfo(request) => { | |
165 | unimplemented!() | |
166 | } | |
167 | RepositoryRequest::IssuesCount(request) => { | |
168 | unimplemented!() | |
169 | } | |
170 | RepositoryRequest::IssueLabels(request) => { | |
171 | unimplemented!() | |
172 | } | |
173 | RepositoryRequest::Issues(request) => { | |
174 | unimplemented!(); | |
175 | } | |
176 | }, | |
177 | RepositoryMessageKind::Response(_response) => { | |
178 | unreachable!() | |
179 | } | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | if let MessageKind::Authentication(authentication) = &message { | |
185 | match authentication { | |
186 | AuthenticationMessage::Request(request) => match request { | |
187 | AuthenticationRequest::AuthenticationToken(token) => { | |
188 | unimplemented!() | |
189 | } | |
190 | AuthenticationRequest::TokenExtension(request) => { | |
191 | unimplemented!() | |
192 | } | |
193 | AuthenticationRequest::RegisterAccount(request) => { | |
194 | unimplemented!() | |
195 | } | |
196 | }, | |
197 | AuthenticationMessage::Response(_) => unreachable!(), | |
198 | } | |
199 | } | |
200 | ||
201 | if let MessageKind::Discovery(message) = &message { | |
202 | let mut backend = discovery_backend.lock().await; | |
203 | backend.try_handle(message).await?; | |
204 | ||
205 | return Ok(()); | |
206 | } | |
207 | ||
208 | if let MessageKind::User(message) = &message { | |
209 | match &message.message { | |
210 | UserMessageKind::Request(request) => match request { | |
211 | UserMessageRequest::DisplayName(request) => { | |
212 | unimplemented!() | |
213 | } | |
214 | UserMessageRequest::DisplayImage(request) => { | |
215 | unimplemented!() | |
216 | } | |
217 | UserMessageRequest::Bio(request) => { | |
218 | unimplemented!() | |
219 | } | |
220 | UserMessageRequest::Repositories(request) => { | |
221 | unimplemented!() | |
222 | } | |
223 | }, | |
224 | UserMessageKind::Response(_) => unreachable!(), | |
225 | } | |
226 | } | |
227 | ||
228 | Ok(()) | |
229 | } | |
230 | ||
231 | async fn send_and_get_listener( | |
232 | message: MessageKind, | |
233 | listeners: &Arc<Mutex<Listeners>>, | |
234 | connections: &Arc<Mutex<Connections>>, | |
235 | ) -> Receiver<MessageKind> { | |
236 | let (instance, user, repository): (Option<Instance>, Option<User>, Option<Repository>) = | |
237 | match &message { | |
238 | MessageKind::Handshake(_) => { | |
239 | todo!() | |
240 | } | |
241 | MessageKind::Repository(repository) => (None, None, Some(repository.target.clone())), | |
242 | MessageKind::Authentication(_) => todo!(), | |
243 | MessageKind::Discovery(_) => todo!(), | |
244 | MessageKind::User(user) => todo!(), | |
245 | MessageKind::Error(_) => todo!(), | |
246 | }; | |
247 | ||
248 | let target = match (&instance, &user, &repository) { | |
249 | (Some(instance), _, _) => instance.clone(), | |
250 | (_, Some(user), _) => user.instance.clone(), | |
251 | (_, _, Some(repository)) => repository.instance.clone(), | |
252 | _ => unreachable!(), | |
253 | }; | |
254 | ||
255 | let mut listeners = listeners.lock().await; | |
256 | let listener = listeners.add(instance, user, repository); | |
257 | drop(listeners); | |
258 | ||
259 | let connections = connections.lock().await; | |
260 | ||
261 | if let Some(connection) = connections.instance_connections.get(&target) { | |
262 | if let Err(_) = connection.sender.send(message) { | |
263 | error!("Error sending message."); | |
264 | } | |
265 | } else { | |
266 | error!("Unable to message {}, this is a bug.", target.url); | |
267 | ||
268 | panic!(); | |
269 | } | |
270 | ||
271 | drop(connections); | |
272 | ||
273 | listener | |
274 | } | |
275 | ||
276 | async fn send<T: Serialize>( | |
277 | socket: &mut WebSocketStream<TcpStream>, | |
278 | message: T, | |
279 | ) -> Result<(), Error> { | |
280 | socket | |
281 | .send(Message::Binary(serde_json::to_vec(&message)?)) | |
282 | .await?; | |
283 | ||
284 | Ok(()) | |
285 | } | |
286 | ||
287 | 49 | #[derive(Debug, thiserror::Error)] |
288 | 50 | #[error("handler did not handle")] |
289 | 51 | pub struct HandlerUnhandled; |
src/connection/authentication.rs
@@ -1,12 +1,11 @@ | ||
1 | 1 | use anyhow::Error; |
2 | 2 | use thiserror::Error; |
3 | 3 | |
4 | use crate::messages::authentication::{AuthenticationMessage, AuthenticationResponse}; | |
5 | use crate::model::authenticated::MessageHandler; | |
6 | use crate::{ | |
7 | messages::{authentication::AuthenticationRequest, MessageKind}, | |
8 | model::authenticated::{AuthenticatedInstance, NetworkMessage, State}, | |
4 | use crate::messages::authentication::{ | |
5 | AuthenticationTokenRequest, RegisterAccountRequest, TokenExtensionRequest, | |
9 | 6 | }; |
7 | use crate::model::authenticated::{AuthenticatedInstance, NetworkMessage, State}; | |
8 | use crate::model::authenticated::{Message, MessageHandler}; | |
10 | 9 | |
11 | 10 | use super::wrapper::ConnectionState; |
12 | 11 | use super::HandlerUnhandled; |
@@ -15,42 +14,34 @@ pub async fn authentication_handle( | ||
15 | 14 | message: &NetworkMessage, |
16 | 15 | state: &ConnectionState, |
17 | 16 | ) -> Result<(), Error> { |
18 | let message_kind: MessageKind = serde_json::from_slice(&message.0).unwrap(); | |
19 | ||
20 | match message_kind { | |
21 | MessageKind::Authentication(AuthenticationMessage::Request(request)) => match request { | |
22 | AuthenticationRequest::RegisterAccount(_) => { | |
23 | register_account_request | |
24 | .handle_message(message, state) | |
25 | .await | |
26 | } | |
27 | AuthenticationRequest::AuthenticationToken(_) => { | |
28 | authentication_token_request | |
29 | .handle_message(message, state) | |
30 | .await | |
31 | } | |
32 | AuthenticationRequest::TokenExtension(_) => { | |
33 | token_extension_request.handle_message(message, state).await | |
34 | } | |
35 | }, | |
36 | _ => Err(Error::from(HandlerUnhandled)), | |
17 | if register_account_request | |
18 | .handle_message(&message, state) | |
19 | .await | |
20 | .is_ok() | |
21 | { | |
22 | Ok(()) | |
23 | } else if authentication_token_request | |
24 | .handle_message(&message, state) | |
25 | .await | |
26 | .is_ok() | |
27 | { | |
28 | Ok(()) | |
29 | } else if token_extension_request | |
30 | .handle_message(&message, state) | |
31 | .await | |
32 | .is_ok() | |
33 | { | |
34 | Ok(()) | |
35 | } else { | |
36 | Err(Error::from(HandlerUnhandled)) | |
37 | 37 | } |
38 | 38 | } |
39 | 39 | |
40 | 40 | async fn register_account_request( |
41 | 41 | State(connection_state): State<ConnectionState>, |
42 | request: MessageKind, | |
42 | Message(request): Message<RegisterAccountRequest>, | |
43 | 43 | instance: AuthenticatedInstance, |
44 | 44 | ) -> Result<(), AuthenticationConnectionError> { |
45 | let request = if let MessageKind::Authentication(AuthenticationMessage::Request( | |
46 | AuthenticationRequest::RegisterAccount(request), | |
47 | )) = request | |
48 | { | |
49 | request | |
50 | } else { | |
51 | return Err(AuthenticationConnectionError::InvalidRequest); | |
52 | }; | |
53 | ||
54 | 45 | if *instance.inner() != connection_state.instance { |
55 | 46 | return Err(AuthenticationConnectionError::SameInstance); |
56 | 47 | } |
@@ -64,9 +55,7 @@ async fn register_account_request( | ||
64 | 55 | drop(user_backend); |
65 | 56 | |
66 | 57 | connection_state |
67 | .send(MessageKind::Authentication( | |
68 | AuthenticationMessage::Response(AuthenticationResponse::RegisterAccount(response)), | |
69 | )) | |
58 | .send(response) | |
70 | 59 | .await |
71 | 60 | .map_err(|e| AuthenticationConnectionError::Sending(e))?; |
72 | 61 | |
@@ -75,18 +64,9 @@ async fn register_account_request( | ||
75 | 64 | |
76 | 65 | async fn authentication_token_request( |
77 | 66 | State(connection_state): State<ConnectionState>, |
78 | request: MessageKind, | |
67 | Message(request): Message<AuthenticationTokenRequest>, | |
79 | 68 | instance: AuthenticatedInstance, |
80 | 69 | ) -> Result<(), AuthenticationConnectionError> { |
81 | let request = if let MessageKind::Authentication(AuthenticationMessage::Request( | |
82 | AuthenticationRequest::AuthenticationToken(request), | |
83 | )) = request | |
84 | { | |
85 | request | |
86 | } else { | |
87 | return Err(AuthenticationConnectionError::InvalidRequest); | |
88 | }; | |
89 | ||
90 | 70 | let issued_for = instance.inner().clone(); |
91 | 71 | |
92 | 72 | let mut token_granter = connection_state.auth_granter.lock().await; |
@@ -97,9 +77,7 @@ async fn authentication_token_request( | ||
97 | 77 | .map_err(|e| AuthenticationConnectionError::TokenIssuance(e))?; |
98 | 78 | |
99 | 79 | connection_state |
100 | .send(MessageKind::Authentication( | |
101 | AuthenticationMessage::Response(AuthenticationResponse::AuthenticationToken(response)), | |
102 | )) | |
80 | .send(response) | |
103 | 81 | .await |
104 | 82 | .map_err(|e| AuthenticationConnectionError::Sending(e))?; |
105 | 83 | |
@@ -108,18 +86,9 @@ async fn authentication_token_request( | ||
108 | 86 | |
109 | 87 | async fn token_extension_request( |
110 | 88 | State(connection_state): State<ConnectionState>, |
111 | request: MessageKind, | |
89 | Message(request): Message<TokenExtensionRequest>, | |
112 | 90 | instance: AuthenticatedInstance, |
113 | 91 | ) -> Result<(), AuthenticationConnectionError> { |
114 | let request = if let MessageKind::Authentication(AuthenticationMessage::Request( | |
115 | AuthenticationRequest::TokenExtension(request), | |
116 | )) = request | |
117 | { | |
118 | request | |
119 | } else { | |
120 | return Err(AuthenticationConnectionError::InvalidRequest); | |
121 | }; | |
122 | ||
123 | 92 | let issued_for = instance.inner().clone(); |
124 | 93 | |
125 | 94 | let mut token_granter = connection_state.auth_granter.lock().await; |
@@ -130,21 +99,13 @@ async fn token_extension_request( | ||
130 | 99 | .map_err(|e| AuthenticationConnectionError::TokenIssuance(e))?; |
131 | 100 | |
132 | 101 | connection_state |
133 | .send(MessageKind::Authentication( | |
134 | AuthenticationMessage::Response(AuthenticationResponse::TokenExtension(response)), | |
135 | )) | |
102 | .send(response) | |
136 | 103 | .await |
137 | 104 | .map_err(|e| AuthenticationConnectionError::Sending(e))?; |
138 | 105 | |
139 | 106 | Ok(()) |
140 | 107 | } |
141 | 108 | |
142 | async fn verify(state: ConnectionState) { | |
143 | register_account_request | |
144 | .handle_message(&NetworkMessage(vec![]), &state) | |
145 | .await; | |
146 | } | |
147 | ||
148 | 109 | #[derive(Debug, Error)] |
149 | 110 | pub enum AuthenticationConnectionError { |
150 | 111 | #[error("the request was invalid")] |
src/connection/handshake.rs
@@ -2,7 +2,6 @@ use std::{str::FromStr, sync::atomic::Ordering}; | ||
2 | 2 | |
3 | 3 | use anyhow::Error; |
4 | 4 | use semver::Version; |
5 | use thiserror::Error; | |
6 | 5 | |
7 | 6 | use crate::model::authenticated::MessageHandler; |
8 | 7 | use crate::{ |
src/connection/repository.rs
@@ -4,7 +4,6 @@ use crate::{ | ||
4 | 4 | messages::repository::{ |
5 | 5 | CreateRepositoryRequest, RepositoryFileInspectRequest, RepositoryInfoRequest, |
6 | 6 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
7 | RepositoryRequest, | |
8 | 7 | }, |
9 | 8 | model::authenticated::{AuthenticatedUser, Message, MessageHandler, NetworkMessage, State}, |
10 | 9 | }; |
@@ -100,25 +99,25 @@ async fn repository_info( | ||
100 | 99 | } |
101 | 100 | |
102 | 101 | async fn issues_count( |
103 | Message(request): Message<RepositoryIssuesCountRequest>, | |
104 | State(connection_state): State<ConnectionState>, | |
105 | user: Option<AuthenticatedUser>, | |
102 | Message(_request): Message<RepositoryIssuesCountRequest>, | |
103 | State(_connection_state): State<ConnectionState>, | |
104 | _user: Option<AuthenticatedUser>, | |
106 | 105 | ) -> Result<(), RepositoryError> { |
107 | 106 | unimplemented!(); |
108 | 107 | } |
109 | 108 | |
110 | 109 | async fn issue_labels( |
111 | Message(request): Message<RepositoryIssueLabelsRequest>, | |
112 | State(connection_state): State<ConnectionState>, | |
113 | user: Option<AuthenticatedUser>, | |
110 | Message(_request): Message<RepositoryIssueLabelsRequest>, | |
111 | State(_connection_state): State<ConnectionState>, | |
112 | _user: Option<AuthenticatedUser>, | |
114 | 113 | ) -> Result<(), RepositoryError> { |
115 | 114 | unimplemented!(); |
116 | 115 | } |
117 | 116 | |
118 | 117 | async fn issues( |
119 | Message(request): Message<RepositoryIssuesRequest>, | |
120 | State(connection_state): State<ConnectionState>, | |
121 | user: Option<AuthenticatedUser>, | |
118 | Message(_request): Message<RepositoryIssuesRequest>, | |
119 | State(_connection_state): State<ConnectionState>, | |
120 | _user: Option<AuthenticatedUser>, | |
122 | 121 | ) -> Result<(), RepositoryError> { |
123 | 122 | unimplemented!(); |
124 | 123 | } |
src/connection/user.rs
@@ -17,6 +17,8 @@ pub async fn user_handle(message: &NetworkMessage, state: &ConnectionState) -> R | ||
17 | 17 | Ok(()) |
18 | 18 | } else if bio.handle_message(&message, state).await.is_ok() { |
19 | 19 | Ok(()) |
20 | } else if repositories.handle_message(&message, state).await.is_ok() { | |
21 | Ok(()) | |
20 | 22 | } else { |
21 | 23 | Err(Error::from(HandlerUnhandled)) |
22 | 24 | } |
src/connection/wrapper.rs
@@ -14,36 +14,30 @@ use tokio_tungstenite::{tungstenite::Message, WebSocketStream}; | ||
14 | 14 | |
15 | 15 | use crate::{ |
16 | 16 | authentication::AuthenticationTokenGranter, |
17 | backend::{DiscoveryBackend, RepositoryBackend, UserBackend}, | |
18 | connection::ConnectionError, | |
19 | listener::Listeners, | |
17 | backend::{RepositoryBackend, UserBackend}, | |
20 | 18 | model::{authenticated::NetworkMessage, instance::Instance}, |
21 | 19 | }; |
22 | 20 | |
23 | 21 | use super::{ |
24 | authentication::authentication_handle, connection_worker, handshake::handshake_handle, | |
22 | authentication::authentication_handle, handshake::handshake_handle, | |
25 | 23 | repository::repository_handle, user::user_handle, Connections, |
26 | 24 | }; |
27 | 25 | |
28 | 26 | pub async fn connection_wrapper( |
29 | mut socket: WebSocketStream<TcpStream>, | |
30 | listeners: Arc<Mutex<Listeners>>, | |
27 | socket: WebSocketStream<TcpStream>, | |
31 | 28 | connections: Arc<Mutex<Connections>>, |
32 | 29 | repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
33 | 30 | user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
34 | 31 | auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
35 | discovery_backend: Arc<Mutex<dyn DiscoveryBackend + Send>>, | |
36 | 32 | addr: SocketAddr, |
37 | 33 | instance: impl ToOwned<Owned = Instance>, |
38 | 34 | ) { |
39 | let mut connection_state = ConnectionState { | |
35 | let connection_state = ConnectionState { | |
40 | 36 | socket: Arc::new(Mutex::new(socket)), |
41 | listeners, | |
42 | 37 | connections, |
43 | 38 | repository_backend, |
44 | 39 | user_backend, |
45 | 40 | auth_granter, |
46 | discovery_backend, | |
47 | 41 | addr, |
48 | 42 | instance: instance.to_owned(), |
49 | 43 | handshaked: Arc::new(AtomicBool::new(false)), |
@@ -62,7 +56,7 @@ pub async fn connection_wrapper( | ||
62 | 56 | Message::Binary(payload) => payload, |
63 | 57 | Message::Ping(_) => { |
64 | 58 | let mut socket = connection_state.socket.lock().await; |
65 | socket.send(Message::Pong(vec![])).await; | |
59 | let _ = socket.send(Message::Pong(vec![])).await; | |
66 | 60 | drop(socket); |
67 | 61 | continue; |
68 | 62 | } |
@@ -105,12 +99,10 @@ pub async fn connection_wrapper( | ||
105 | 99 | #[derive(Clone)] |
106 | 100 | pub struct ConnectionState { |
107 | 101 | socket: Arc<Mutex<WebSocketStream<TcpStream>>>, |
108 | pub listeners: Arc<Mutex<Listeners>>, | |
109 | 102 | pub connections: Arc<Mutex<Connections>>, |
110 | 103 | pub repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
111 | 104 | pub user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
112 | 105 | pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
113 | pub discovery_backend: Arc<Mutex<dyn DiscoveryBackend + Send>>, | |
114 | 106 | pub addr: SocketAddr, |
115 | 107 | pub instance: Instance, |
116 | 108 | pub handshaked: Arc<AtomicBool>, |
src/lib.rs
@@ -6,7 +6,6 @@ pub mod authentication; | ||
6 | 6 | pub mod backend; |
7 | 7 | pub mod connection; |
8 | 8 | pub mod handshake; |
9 | pub mod listener; | |
10 | 9 | pub mod messages; |
11 | 10 | pub mod model; |
12 | 11 |
src/main.rs
@@ -2,15 +2,10 @@ use anyhow::Error; | ||
2 | 2 | use connection::{Connections, RawConnection}; |
3 | 3 | use giterated_daemon::{ |
4 | 4 | authentication::AuthenticationTokenGranter, |
5 | backend::{ | |
6 | discovery::GiteratedDiscoveryProtocol, git::GitBackend, user::UserAuth, DiscoveryBackend, | |
7 | RepositoryBackend, UserBackend, | |
8 | }, | |
5 | backend::{git::GitBackend, user::UserAuth, RepositoryBackend, UserBackend}, | |
9 | 6 | connection::{self, wrapper::connection_wrapper}, |
10 | listener, | |
11 | 7 | model::instance::Instance, |
12 | 8 | }; |
13 | use listener::Listeners; | |
14 | 9 | use sqlx::{postgres::PgConnectOptions, ConnectOptions, PgPool}; |
15 | 10 | use std::{net::SocketAddr, str::FromStr, sync::Arc}; |
16 | 11 | use tokio::{ |
@@ -30,7 +25,6 @@ async fn main() -> Result<(), Error> { | ||
30 | 25 | tracing_subscriber::fmt::init(); |
31 | 26 | let mut listener = TcpListener::bind("0.0.0.0:7270").await?; |
32 | 27 | let connections: Arc<Mutex<Connections>> = Arc::default(); |
33 | let listeners: Arc<Mutex<Listeners>> = Arc::default(); | |
34 | 28 | let config: Table = { |
35 | 29 | let mut file = File::open("Giterated.toml").await?; |
36 | 30 | let mut text = String::new(); |
@@ -72,11 +66,6 @@ async fn main() -> Result<(), Error> { | ||
72 | 66 | token_granter.clone(), |
73 | 67 | ))); |
74 | 68 | |
75 | let discovery_backend: Arc<Mutex<dyn DiscoveryBackend + Send>> = | |
76 | Arc::new(Mutex::new(GiteratedDiscoveryProtocol { | |
77 | pool: db_pool.clone(), | |
78 | })); | |
79 | ||
80 | 69 | info!("Connected"); |
81 | 70 | |
82 | 71 | loop { |
@@ -111,12 +100,10 @@ async fn main() -> Result<(), Error> { | ||
111 | 100 | let connection = RawConnection { |
112 | 101 | task: tokio::spawn(connection_wrapper( |
113 | 102 | connection, |
114 | listeners.clone(), | |
115 | 103 | connections.clone(), |
116 | 104 | repository_backend.clone(), |
117 | 105 | user_backend.clone(), |
118 | 106 | token_granter.clone(), |
119 | discovery_backend.clone(), | |
120 | 107 | address, |
121 | 108 | Instance::from_str("giterated.dev").unwrap(), |
122 | 109 | )), |
src/messages/authentication.rs
@@ -2,61 +2,11 @@ use serde::{Deserialize, Serialize}; | ||
2 | 2 | |
3 | 3 | use crate::model::authenticated::UserAuthenticationToken; |
4 | 4 | |
5 | use super::InstanceAuthenticated; | |
6 | ||
7 | /// An authentication message. | |
5 | /// An account registration request. | |
8 | 6 | /// |
9 | /// View request documentation, authentication, and authorization | |
10 | /// details in the associated type, [`AuthenticationRequest`]. | |
11 | #[derive(Clone, Serialize, Deserialize)] | |
12 | pub enum AuthenticationMessage { | |
13 | Request(AuthenticationRequest), | |
14 | Response(AuthenticationResponse), | |
15 | } | |
16 | ||
17 | #[derive(Clone, Serialize, Deserialize)] | |
18 | pub enum AuthenticationRequest { | |
19 | /// An account registration request. | |
20 | /// | |
21 | /// # Authentication | |
22 | /// - Instance Authentication | |
23 | /// - **ONLY ACCEPTED WHEN SAME-INSTANCE** | |
24 | RegisterAccount(RegisterAccountRequest), | |
25 | ||
26 | /// An authentication token request. | |
27 | /// | |
28 | /// AKA Login Request | |
29 | /// | |
30 | /// # Authentication | |
31 | /// - Instance Authentication | |
32 | /// - Identifies the Instance to issue the token for | |
33 | /// # Authorization | |
34 | /// - Credentials ([`crate::backend::AuthBackend`]-based) | |
35 | /// - Identifies the User account to issue a token for | |
36 | /// - Decrypts user private key to issue to | |
37 | AuthenticationToken(AuthenticationTokenRequest), | |
38 | ||
39 | /// An authentication token extension request. | |
40 | /// | |
41 | /// # Authentication | |
42 | /// - Instance Authentication | |
43 | /// - Identifies the Instance to issue the token for | |
44 | /// - User Authentication | |
45 | /// - Authenticates the validity of the token | |
46 | /// # Authorization | |
47 | /// - Token-based | |
48 | /// - Validates authorization using token's authenticity | |
49 | TokenExtension(TokenExtensionRequest), | |
50 | } | |
51 | ||
52 | #[derive(Clone, Serialize, Deserialize)] | |
53 | pub enum AuthenticationResponse { | |
54 | RegisterAccount(RegisterAccountResponse), | |
55 | AuthenticationToken(AuthenticationTokenResponse), | |
56 | TokenExtension(TokenExtensionResponse), | |
57 | } | |
58 | ||
59 | /// See [`AuthenticationRequest::RegisterAccount`]'s documentation. | |
7 | /// # Authentication | |
8 | /// - Instance Authentication | |
9 | /// - **ONLY ACCEPTED WHEN SAME-INSTANCE** | |
60 | 10 | #[derive(Clone, Serialize, Deserialize)] |
61 | 11 | pub struct RegisterAccountRequest { |
62 | 12 | pub username: String, |
@@ -69,7 +19,17 @@ pub struct RegisterAccountResponse { | ||
69 | 19 | pub token: String, |
70 | 20 | } |
71 | 21 | |
72 | /// See [`AuthenticationRequest::AuthenticationToken`]'s documentation. | |
22 | /// An authentication token request. | |
23 | /// | |
24 | /// AKA Login Request | |
25 | /// | |
26 | /// # Authentication | |
27 | /// - Instance Authentication | |
28 | /// - Identifies the Instance to issue the token for | |
29 | /// # Authorization | |
30 | /// - Credentials ([`crate::backend::AuthBackend`]-based) | |
31 | /// - Identifies the User account to issue a token for | |
32 | /// - Decrypts user private key to issue to | |
73 | 33 | #[derive(Clone, Serialize, Deserialize)] |
74 | 34 | pub struct AuthenticationTokenRequest { |
75 | 35 | pub username: String, |
@@ -81,7 +41,16 @@ pub struct AuthenticationTokenResponse { | ||
81 | 41 | pub token: String, |
82 | 42 | } |
83 | 43 | |
84 | /// See [`AuthenticationRequest::TokenExtension`]'s documentation. | |
44 | /// An authentication token extension request. | |
45 | /// | |
46 | /// # Authentication | |
47 | /// - Instance Authentication | |
48 | /// - Identifies the Instance to issue the token for | |
49 | /// - User Authentication | |
50 | /// - Authenticates the validity of the token | |
51 | /// # Authorization | |
52 | /// - Token-based | |
53 | /// - Validates authorization using token's authenticity | |
85 | 54 | #[derive(Clone, Serialize, Deserialize)] |
86 | 55 | pub struct TokenExtensionRequest { |
87 | 56 | pub token: UserAuthenticationToken, |
src/messages/discovery.rs
@@ -3,20 +3,6 @@ use serde::{Deserialize, Serialize}; | ||
3 | 3 | |
4 | 4 | use crate::model::discovery::DiscoveryItem; |
5 | 5 | |
6 | use super::InstanceAuthenticated; | |
7 | ||
8 | #[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] | |
9 | pub struct DiscoveryMessage { | |
10 | pub message: InstanceAuthenticated<DiscoveryMessageKind>, | |
11 | } | |
12 | ||
13 | #[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] | |
14 | pub enum DiscoveryMessageKind { | |
15 | Offer(DiscoveryOffer), | |
16 | Request(DiscoveryRequest), | |
17 | Discoveries(Discoveries), | |
18 | } | |
19 | ||
20 | 6 | #[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] |
21 | 7 | pub struct DiscoveryOffer { |
22 | 8 | pub earliest: DateTime<Utc>, |
src/messages/mod.rs
@@ -1,25 +1,7 @@ | ||
1 | use anyhow::Error; | |
2 | use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; | |
3 | use rsa::{ | |
4 | pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, | |
5 | pss::{Signature, SigningKey, VerifyingKey}, | |
6 | sha2::Sha256, | |
7 | signature::{RandomizedSigner, SignatureEncoding, Verifier}, | |
8 | RsaPrivateKey, RsaPublicKey, | |
9 | }; | |
10 | 1 | use serde::{Deserialize, Serialize}; |
11 | 2 | use std::fmt::Debug; |
12 | 3 | |
13 | use crate::{ | |
14 | authentication::UserTokenMetadata, | |
15 | handshake::HandshakeMessage, | |
16 | model::{instance::Instance, user::User}, | |
17 | }; | |
18 | ||
19 | use self::{ | |
20 | authentication::AuthenticationMessage, discovery::DiscoveryMessage, | |
21 | repository::RepositoryMessage, user::UserMessage, | |
22 | }; | |
4 | use crate::model::user::User; | |
23 | 5 | |
24 | 6 | pub mod authentication; |
25 | 7 | pub mod discovery; |
@@ -27,16 +9,6 @@ pub mod issues; | ||
27 | 9 | pub mod repository; |
28 | 10 | pub mod user; |
29 | 11 | |
30 | #[derive(Clone, Serialize, Deserialize)] | |
31 | pub enum MessageKind { | |
32 | Handshake(HandshakeMessage), | |
33 | Repository(RepositoryMessage), | |
34 | Authentication(AuthenticationMessage), | |
35 | Discovery(DiscoveryMessage), | |
36 | User(UserMessage), | |
37 | Error(ErrorMessage), | |
38 | } | |
39 | ||
40 | 12 | #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] |
41 | 13 | pub enum ErrorMessage { |
42 | 14 | #[error("user {0} doesn't exist or isn't valid in this context")] |
@@ -44,262 +16,3 @@ pub enum ErrorMessage { | ||
44 | 16 | #[error("internal error: shutdown")] |
45 | 17 | Shutdown, |
46 | 18 | } |
47 | ||
48 | /// An authenticated message, where the instance is authenticating | |
49 | /// a request it is making for itself. | |
50 | #[derive(Serialize, Deserialize)] | |
51 | pub struct InstanceAuthenticated<T: Serialize> { | |
52 | message: T, | |
53 | pub instance: Instance, | |
54 | signature: Vec<u8>, | |
55 | } | |
56 | ||
57 | impl<T> PartialEq for InstanceAuthenticated<T> | |
58 | where | |
59 | T: PartialEq + Serialize, | |
60 | { | |
61 | fn eq(&self, other: &Self) -> bool { | |
62 | self.message == other.message | |
63 | && self.instance == other.instance | |
64 | && self.signature == other.signature | |
65 | } | |
66 | } | |
67 | ||
68 | impl<T> Eq for InstanceAuthenticated<T> where T: Eq + Serialize {} | |
69 | ||
70 | impl<T> std::hash::Hash for InstanceAuthenticated<T> | |
71 | where | |
72 | T: std::hash::Hash + Serialize, | |
73 | { | |
74 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | |
75 | self.message.hash(state); | |
76 | self.instance.hash(state); | |
77 | self.signature.hash(state); | |
78 | } | |
79 | } | |
80 | ||
81 | impl<T> Clone for InstanceAuthenticated<T> | |
82 | where | |
83 | T: Clone + Serialize, | |
84 | { | |
85 | fn clone(&self) -> Self { | |
86 | Self { | |
87 | message: self.message.clone(), | |
88 | instance: self.instance.clone(), | |
89 | signature: self.signature.clone(), | |
90 | } | |
91 | } | |
92 | } | |
93 | ||
94 | impl<T> Debug for InstanceAuthenticated<T> | |
95 | where | |
96 | T: Debug + Serialize, | |
97 | { | |
98 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
99 | f.debug_struct("Authenticated") | |
100 | .field("message", &self.message) | |
101 | .field("instance", &self.instance) | |
102 | .field("signature", &self.signature) | |
103 | .finish() | |
104 | } | |
105 | } | |
106 | ||
107 | impl<T: Serialize> InstanceAuthenticated<T> { | |
108 | pub fn new(message: T, instance: Instance, private_key: String) -> Result<Self, Error> { | |
109 | let mut rng = rand::thread_rng(); | |
110 | ||
111 | let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key)?; | |
112 | let signing_key = SigningKey::<Sha256>::new(private_key); | |
113 | ||
114 | let message_json = serde_json::to_vec(&message)?; | |
115 | ||
116 | let signature = signing_key.sign_with_rng(&mut rng, &message_json); | |
117 | ||
118 | Ok(Self { | |
119 | message, | |
120 | instance, | |
121 | signature: signature.to_vec(), | |
122 | }) | |
123 | } | |
124 | ||
125 | pub async fn inner(&self) -> &T { | |
126 | &self.message | |
127 | } | |
128 | ||
129 | pub async fn validate(&self, instance: &Instance) -> Result<(), Error> { | |
130 | let public_key = public_key(instance).await?; | |
131 | let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap(); | |
132 | ||
133 | let verifying_key: VerifyingKey<Sha256> = VerifyingKey::new(public_key); | |
134 | ||
135 | let message_json = serde_json::to_vec(&self.message).unwrap(); | |
136 | ||
137 | verifying_key | |
138 | .verify( | |
139 | &message_json, | |
140 | &Signature::try_from(self.signature.as_ref()).unwrap(), | |
141 | ) | |
142 | .unwrap(); | |
143 | ||
144 | Ok(()) | |
145 | } | |
146 | } | |
147 | ||
148 | /// An authenticated message. | |
149 | /// | |
150 | /// Includes the message, with a digest generated with | |
151 | /// our private key. | |
152 | #[derive(Serialize, Deserialize)] | |
153 | pub struct ValidatedUserAuthenticated<T: Serialize> { | |
154 | #[serde(flatten)] | |
155 | message: T, | |
156 | pub(crate) user: User, | |
157 | } | |
158 | ||
159 | impl<T> Clone for ValidatedUserAuthenticated<T> | |
160 | where | |
161 | T: Clone + Serialize, | |
162 | { | |
163 | fn clone(&self) -> Self { | |
164 | Self { | |
165 | message: self.message.clone(), | |
166 | user: self.user.clone(), | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
171 | impl<T> Debug for ValidatedUserAuthenticated<T> | |
172 | where | |
173 | T: Debug + Serialize, | |
174 | { | |
175 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
176 | f.debug_struct("Authenticated") | |
177 | .field("message", &self.message) | |
178 | .field("user", &self.user) | |
179 | .finish() | |
180 | } | |
181 | } | |
182 | ||
183 | impl<T: Serialize> ValidatedUserAuthenticated<T> { | |
184 | pub async fn inner(&self) -> &T { | |
185 | &self.message | |
186 | } | |
187 | ||
188 | pub async fn user(&self) -> &User { | |
189 | &self.user | |
190 | } | |
191 | } | |
192 | ||
193 | /// An unvalidated authenticated message. | |
194 | /// | |
195 | /// Includes the message, with a digest generated with | |
196 | /// our private key. | |
197 | #[derive(Serialize, Deserialize)] | |
198 | pub struct UnvalidatedUserAuthenticated<T: Serialize> { | |
199 | #[serde(flatten)] | |
200 | message: T, | |
201 | token: String, | |
202 | digest: Vec<u8>, | |
203 | } | |
204 | ||
205 | impl<T> Clone for UnvalidatedUserAuthenticated<T> | |
206 | where | |
207 | T: Clone + Serialize, | |
208 | { | |
209 | fn clone(&self) -> Self { | |
210 | Self { | |
211 | message: self.message.clone(), | |
212 | token: self.token.clone(), | |
213 | digest: self.digest.clone(), | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | impl<T> Debug for UnvalidatedUserAuthenticated<T> | |
219 | where | |
220 | T: Debug + Serialize, | |
221 | { | |
222 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
223 | f.debug_struct("Authenticated") | |
224 | .field("message", &self.message) | |
225 | .field("token", &self.token) | |
226 | .field("digest", &self.digest) | |
227 | .finish() | |
228 | } | |
229 | } | |
230 | ||
231 | impl<T: Serialize> UnvalidatedUserAuthenticated<T> { | |
232 | pub fn new(message: T, token: String, private_key: String) -> Result<Self, Error> { | |
233 | let mut rng = rand::thread_rng(); | |
234 | ||
235 | let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key)?; | |
236 | let signing_key = SigningKey::<Sha256>::new(private_key); | |
237 | ||
238 | let message_json = serde_json::to_vec(&message)?; | |
239 | ||
240 | let signature = signing_key.sign_with_rng(&mut rng, &message_json); | |
241 | ||
242 | Ok(Self { | |
243 | message, | |
244 | token, | |
245 | digest: signature.to_vec(), | |
246 | }) | |
247 | } | |
248 | ||
249 | pub async fn inner(&self) -> &T { | |
250 | &self.message | |
251 | } | |
252 | ||
253 | pub async fn validate(self) -> Result<ValidatedUserAuthenticated<T>, Error> { | |
254 | let instance = { | |
255 | let mut validation = Validation::new(Algorithm::RS256); | |
256 | validation.insecure_disable_signature_validation(); | |
257 | ||
258 | info!("Value: {:?}", self.token); | |
259 | ||
260 | let value: TokenData<UserTokenMetadata> = | |
261 | decode(&self.token, &DecodingKey::from_secret(b"test"), &validation).unwrap(); | |
262 | ||
263 | value.claims.generated_for.clone() | |
264 | }; | |
265 | ||
266 | let public_key_raw = public_key(&instance).await?; | |
267 | let public_key = RsaPublicKey::from_pkcs1_pem(&public_key_raw).unwrap(); | |
268 | ||
269 | let verifying_key: VerifyingKey<Sha256> = VerifyingKey::new(public_key); | |
270 | ||
271 | let message_json = serde_json::to_vec(&self.message).unwrap(); | |
272 | ||
273 | verifying_key | |
274 | .verify( | |
275 | &message_json, | |
276 | &Signature::try_from(self.digest.as_ref()).unwrap(), | |
277 | ) | |
278 | .unwrap(); | |
279 | ||
280 | let verification_key = DecodingKey::from_rsa_pem(public_key_raw.as_bytes()).unwrap(); | |
281 | ||
282 | let data: TokenData<UserTokenMetadata> = decode( | |
283 | &self.token, | |
284 | &verification_key, | |
285 | &Validation::new(Algorithm::RS256), | |
286 | ) | |
287 | .unwrap(); | |
288 | ||
289 | assert_eq!(data.claims.generated_for, instance); | |
290 | ||
291 | Ok(ValidatedUserAuthenticated { | |
292 | message: self.message, | |
293 | user: data.claims.user, | |
294 | }) | |
295 | } | |
296 | } | |
297 | ||
298 | async fn public_key(instance: &Instance) -> Result<String, Error> { | |
299 | let key = reqwest::get(format!("https://{}/.giterated/pubkey.pem", instance.url)) | |
300 | .await? | |
301 | .text() | |
302 | .await?; | |
303 | ||
304 | Ok(key) | |
305 | } |
src/messages/repository.rs
@@ -2,112 +2,23 @@ use serde::{Deserialize, Serialize}; | ||
2 | 2 | |
3 | 3 | use crate::model::repository::RepositoryVisibility; |
4 | 4 | use crate::model::{ |
5 | repository::{Commit, Repository, RepositoryTreeEntry, RepositoryView}, | |
5 | repository::{Commit, Repository, RepositoryTreeEntry}, | |
6 | 6 | user::User, |
7 | 7 | }; |
8 | 8 | |
9 | use super::UnvalidatedUserAuthenticated; | |
10 | ||
11 | /// A repository message. | |
9 | /// A request to create a repository. | |
12 | 10 | /// |
13 | /// View request documentation, authentication, and authorization | |
14 | /// details in the associated type, [`RepositoryRequest`]. | |
15 | #[derive(Clone, Serialize, Deserialize)] | |
16 | pub struct RepositoryMessage { | |
17 | pub target: Repository, | |
18 | pub command: RepositoryMessageKind, | |
19 | } | |
20 | ||
21 | #[derive(Clone, Serialize, Deserialize)] | |
22 | pub enum RepositoryMessageKind { | |
23 | Request(RepositoryRequest), | |
24 | Response(RepositoryResponse), | |
25 | } | |
26 | ||
27 | #[derive(Clone, Serialize, Deserialize)] | |
28 | pub enum RepositoryRequest { | |
29 | /// A request to create a repository. | |
30 | /// | |
31 | /// # Authentication | |
32 | /// - Instance Authentication | |
33 | /// - Used to validate User token `issued_for` | |
34 | /// - User Authentication | |
35 | /// - Used to source owning user | |
36 | /// - Used to authorize user token against user's instance | |
37 | /// # Authorization | |
38 | /// - Instance Authorization | |
39 | /// - Used to authorize action using User token requiring a correct `issued_for` and valid issuance from user's instance | |
40 | /// - User Authorization | |
41 | /// - Potential User permissions checks | |
42 | CreateRepository(UnvalidatedUserAuthenticated<CreateRepositoryRequest>), | |
43 | ||
44 | /// A request to inspect the tree of a repository. | |
45 | /// | |
46 | /// # Authentication | |
47 | /// - Instance Authentication | |
48 | /// - Validate request against the `issued_for` public key | |
49 | /// - Validate User token against the user's instance's public key | |
50 | /// # Authorization | |
51 | /// - User Authorization | |
52 | /// - Potential User permissions checks | |
53 | RepositoryFileInspect(UnvalidatedUserAuthenticated<RepositoryFileInspectRequest>), | |
54 | ||
55 | /// A request to get a repository's information. | |
56 | /// | |
57 | /// # Authentication | |
58 | /// - Instance Authentication | |
59 | /// - Validate request against the `issued_for` public key | |
60 | /// - Validate User token against the user's instance's public key | |
61 | /// # Authorization | |
62 | /// - User Authorization | |
63 | /// - Potential User permissions checks | |
64 | RepositoryInfo(UnvalidatedUserAuthenticated<RepositoryInfoRequest>), | |
65 | ||
66 | /// A request to get a repository's issues count. | |
67 | /// | |
68 | /// # Authentication | |
69 | /// - Instance Authentication | |
70 | /// - Validate request against the `issued_for` public key | |
71 | /// - Validate User token against the user's instance's public key | |
72 | /// # Authorization | |
73 | /// - User Authorization | |
74 | /// - Potential User permissions checks | |
75 | IssuesCount(UnvalidatedUserAuthenticated<RepositoryIssuesCountRequest>), | |
76 | ||
77 | /// A request to get a repository's issue labels. | |
78 | /// | |
79 | /// # Authentication | |
80 | /// - Instance Authentication | |
81 | /// - Validate request against the `issued_for` public key | |
82 | /// - Validate User token against the user's instance's public key | |
83 | /// # Authorization | |
84 | /// - User Authorization | |
85 | /// - Potential User permissions checks | |
86 | IssueLabels(UnvalidatedUserAuthenticated<RepositoryIssueLabelsRequest>), | |
87 | ||
88 | /// A request to get a repository's issues. | |
89 | /// | |
90 | /// # Authentication | |
91 | /// - Instance Authentication | |
92 | /// - Validate request against the `issued_for` public key | |
93 | /// - Validate User token against the user's instance's public key | |
94 | /// # Authorization | |
95 | /// - User Authorization | |
96 | /// - Potential User permissions checks | |
97 | Issues(UnvalidatedUserAuthenticated<RepositoryIssuesRequest>), | |
98 | } | |
99 | ||
100 | #[derive(Clone, Serialize, Deserialize)] | |
101 | pub enum RepositoryResponse { | |
102 | CreateRepository(CreateRepositoryResponse), | |
103 | RepositoryFileInspection(RepositoryFileInspectionResponse), | |
104 | RepositoryInfo(RepositoryView), | |
105 | IssuesCount(RepositoryIssuesCountResponse), | |
106 | IssueLabels(RepositoryIssueLabelsResponse), | |
107 | Issues(RepositoryIssuesResponse), | |
108 | } | |
109 | ||
110 | /// See [`RepositoryRequest::CreateRepository`]'s documentation. | |
11 | /// # Authentication | |
12 | /// - Instance Authentication | |
13 | /// - Used to validate User token `issued_for` | |
14 | /// - User Authentication | |
15 | /// - Used to source owning user | |
16 | /// - Used to authorize user token against user's instance | |
17 | /// # Authorization | |
18 | /// - Instance Authorization | |
19 | /// - Used to authorize action using User token requiring a correct `issued_for` and valid issuance from user's instance | |
20 | /// - User Authorization | |
21 | /// - Potential User permissions checks | |
111 | 22 | #[derive(Clone, Serialize, Deserialize)] |
112 | 23 | pub struct CreateRepositoryRequest { |
113 | 24 | pub name: String, |
@@ -123,7 +34,15 @@ pub enum CreateRepositoryResponse { | ||
123 | 34 | Failed, |
124 | 35 | } |
125 | 36 | |
126 | /// See [`RepositoryRequest::RepositoryFileInspect`]'s documentation. | |
37 | /// A request to inspect the tree of a repository. | |
38 | /// | |
39 | /// # Authentication | |
40 | /// - Instance Authentication | |
41 | /// - Validate request against the `issued_for` public key | |
42 | /// - Validate User token against the user's instance's public key | |
43 | /// # Authorization | |
44 | /// - User Authorization | |
45 | /// - Potential User permissions checks | |
127 | 46 | #[derive(Clone, Serialize, Deserialize)] |
128 | 47 | pub struct RepositoryFileInspectRequest { |
129 | 48 | pub path: RepositoryTreeEntry, |
@@ -143,7 +62,15 @@ pub enum RepositoryFileInspectionResponse { | ||
143 | 62 | }, |
144 | 63 | } |
145 | 64 | |
146 | /// See [`RepositoryRequest::IssuesCount`]'s documentation. | |
65 | /// A request to get a repository's information. | |
66 | /// | |
67 | /// # Authentication | |
68 | /// - Instance Authentication | |
69 | /// - Validate request against the `issued_for` public key | |
70 | /// - Validate User token against the user's instance's public key | |
71 | /// # Authorization | |
72 | /// - User Authorization | |
73 | /// - Potential User permissions checks | |
147 | 74 | #[derive(Clone, Serialize, Deserialize)] |
148 | 75 | pub struct RepositoryIssuesCountRequest; |
149 | 76 | |
@@ -152,7 +79,15 @@ pub struct RepositoryIssuesCountResponse { | ||
152 | 79 | pub count: u64, |
153 | 80 | } |
154 | 81 | |
155 | /// See [`RepositoryRequest::IssueLabels`]'s documentation. | |
82 | /// A request to get a repository's issues count. | |
83 | /// | |
84 | /// # Authentication | |
85 | /// - Instance Authentication | |
86 | /// - Validate request against the `issued_for` public key | |
87 | /// - Validate User token against the user's instance's public key | |
88 | /// # Authorization | |
89 | /// - User Authorization | |
90 | /// - Potential User permissions checks | |
156 | 91 | #[derive(Clone, Serialize, Deserialize)] |
157 | 92 | pub struct RepositoryIssueLabelsRequest; |
158 | 93 | |
@@ -167,7 +102,15 @@ pub struct IssueLabel { | ||
167 | 102 | pub color: String, |
168 | 103 | } |
169 | 104 | |
170 | /// See [`RepositoryRequest::Issues`]'s documentation. | |
105 | /// A request to get a repository's issue labels. | |
106 | /// | |
107 | /// # Authentication | |
108 | /// - Instance Authentication | |
109 | /// - Validate request against the `issued_for` public key | |
110 | /// - Validate User token against the user's instance's public key | |
111 | /// # Authorization | |
112 | /// - User Authorization | |
113 | /// - Potential User permissions checks | |
171 | 114 | #[derive(Clone, Serialize, Deserialize)] |
172 | 115 | pub struct RepositoryIssuesRequest; |
173 | 116 | |
@@ -176,6 +119,15 @@ pub struct RepositoryIssuesResponse { | ||
176 | 119 | pub issues: Vec<RepositoryIssue>, |
177 | 120 | } |
178 | 121 | |
122 | /// A request to get a repository's issues. | |
123 | /// | |
124 | /// # Authentication | |
125 | /// - Instance Authentication | |
126 | /// - Validate request against the `issued_for` public key | |
127 | /// - Validate User token against the user's instance's public key | |
128 | /// # Authorization | |
129 | /// - User Authorization | |
130 | /// - Potential User permissions checks | |
179 | 131 | #[derive(Clone, Serialize, Deserialize)] |
180 | 132 | pub struct RepositoryIssue { |
181 | 133 | pub author: User, |
src/messages/user.rs
@@ -1,38 +1,6 @@ | ||
1 | 1 | use serde::{Deserialize, Serialize}; |
2 | 2 | |
3 | use crate::model::{ | |
4 | instance::Instance, | |
5 | repository::{Repository, RepositorySummary}, | |
6 | user::User, | |
7 | }; | |
8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
10 | pub struct UserMessage { | |
11 | pub instance: Instance, | |
12 | pub message: UserMessageKind, | |
13 | } | |
14 | ||
15 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
16 | pub enum UserMessageKind { | |
17 | Request(UserMessageRequest), | |
18 | Response(UserMessageResponse), | |
19 | } | |
20 | ||
21 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
22 | pub enum UserMessageRequest { | |
23 | DisplayName(UserDisplayNameRequest), | |
24 | DisplayImage(UserDisplayImageRequest), | |
25 | Bio(UserBioRequest), | |
26 | Repositories(UserRepositoriesRequest), | |
27 | } | |
28 | ||
29 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
30 | pub enum UserMessageResponse { | |
31 | DisplayName(UserDisplayNameResponse), | |
32 | DisplayImage(UserDisplayImageResponse), | |
33 | Bio(UserBioResponse), | |
34 | Repositories(UserRepositoriesResponse), | |
35 | } | |
3 | use crate::model::{repository::RepositorySummary, user::User}; | |
36 | 4 | |
37 | 5 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] |
38 | 6 | pub struct UserDisplayNameRequest { |
src/model/authenticated.rs
@@ -1,14 +1,12 @@ | ||
1 | use std::{any::type_name, ops::Deref, pin::Pin, str::FromStr}; | |
1 | use std::{any::type_name, collections::HashMap, ops::Deref}; | |
2 | 2 | |
3 | 3 | use anyhow::Error; |
4 | use futures_util::{future::BoxFuture, Future, FutureExt}; | |
4 | use futures_util::Future; | |
5 | 5 | use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; |
6 | use rsa::{pkcs1::DecodeRsaPublicKey, RsaPublicKey}; | |
7 | 6 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; |
7 | use serde_json::Value; | |
8 | 8 | |
9 | use crate::{ | |
10 | authentication::UserTokenMetadata, connection::wrapper::ConnectionState, messages::MessageKind, | |
11 | }; | |
9 | use crate::{authentication::UserTokenMetadata, connection::wrapper::ConnectionState}; | |
12 | 10 | |
13 | 11 | use super::{instance::Instance, user::User}; |
14 | 12 | |
@@ -88,7 +86,7 @@ pub struct UserAuthenticator { | ||
88 | 86 | } |
89 | 87 | |
90 | 88 | impl AuthenticationSourceProvider for UserAuthenticator { |
91 | fn authenticate(self, payload: &Vec<u8>) -> AuthenticationSource { | |
89 | fn authenticate(self, _payload: &Vec<u8>) -> AuthenticationSource { | |
92 | 90 | AuthenticationSource::User { |
93 | 91 | user: self.user, |
94 | 92 | token: self.token, |
@@ -103,7 +101,7 @@ pub struct InstanceAuthenticator<'a> { | ||
103 | 101 | } |
104 | 102 | |
105 | 103 | impl AuthenticationSourceProvider for InstanceAuthenticator<'_> { |
106 | fn authenticate(self, payload: &Vec<u8>) -> AuthenticationSource { | |
104 | fn authenticate(self, _payload: &Vec<u8>) -> AuthenticationSource { | |
107 | 105 | todo!() |
108 | 106 | } |
109 | 107 | } |
@@ -188,7 +186,7 @@ impl FromMessage<ConnectionState> for AuthenticatedUser { | ||
188 | 186 | network_message: &NetworkMessage, |
189 | 187 | state: &ConnectionState, |
190 | 188 | ) -> Result<Self, Error> { |
191 | let message: Authenticated<MessageKind> = | |
189 | let message: Authenticated<HashMap<String, Value>> = | |
192 | 190 | serde_json::from_slice(&network_message).map_err(|e| Error::from(e))?; |
193 | 191 | |
194 | 192 | let (auth_user, auth_token) = message |
@@ -230,18 +228,8 @@ impl FromMessage<ConnectionState> for AuthenticatedUser { | ||
230 | 228 | #[async_trait::async_trait] |
231 | 229 | impl FromMessage<ConnectionState> for AuthenticatedInstance { |
232 | 230 | async fn from_message( |
233 | message: &NetworkMessage, | |
234 | state: &ConnectionState, | |
235 | ) -> Result<Self, Error> { | |
236 | todo!() | |
237 | } | |
238 | } | |
239 | ||
240 | #[async_trait::async_trait] | |
241 | impl FromMessage<ConnectionState> for MessageKind { | |
242 | async fn from_message( | |
243 | message: &NetworkMessage, | |
244 | state: &ConnectionState, | |
231 | _message: &NetworkMessage, | |
232 | _state: &ConnectionState, | |
245 | 233 | ) -> Result<Self, Error> { |
246 | 234 | todo!() |
247 | 235 | } |