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

ambee/giterated-api

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

Many things

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨d782588

⁨src/lib.rs⁩ - ⁨15770⁩ bytes
Raw
1 use std::convert::Infallible;
2 use std::str::FromStr;
3 use std::{error::Error, net::SocketAddr};
4
5 use futures_util::{SinkExt, StreamExt};
6 use giterated_daemon::messages::authentication::{RegisterAccountRequest, RegisterAccountResponse};
7 use giterated_daemon::messages::UnvalidatedUserAuthenticated;
8 use giterated_daemon::model::repository::RepositoryVisibility;
9 use giterated_daemon::model::user::User;
10 use giterated_daemon::{version, validate_version};
11 use giterated_daemon::{
12 handshake::{HandshakeFinalize, HandshakeMessage, InitiateHandshake},
13 messages::{
14 authentication::{
15 AuthenticationMessage, AuthenticationRequest, AuthenticationResponse,
16 AuthenticationTokenRequest, TokenExtensionRequest,
17 },
18 repository::{
19 CreateRepositoryRequest, RepositoryInfoRequest, RepositoryMessage,
20 RepositoryMessageKind, RepositoryRequest, RepositoryResponse,
21 },
22 InstanceAuthenticated, MessageKind,
23 },
24 model::{
25 instance::Instance,
26 repository::{Repository, RepositoryView},
27 },
28 };
29 use serde::Serialize;
30 use tokio::net::TcpStream;
31 use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
32
33 type Socket = WebSocketStream<MaybeTlsStream<TcpStream>>;
34
35 #[macro_use]
36 extern crate tracing;
37
38 pub struct GiteratedApiBuilder {
39 our_instance: Instance,
40 our_private_key: Option<String>,
41 our_public_key: Option<String>,
42 target_instance: Option<Instance>,
43 }
44
45 pub trait AsInstance {
46 type Error: Error + Send + Sync + 'static;
47
48 fn into_instance(self) -> Result<Instance, Self::Error>;
49 }
50
51 impl AsInstance for &str {
52 type Error = <Instance as FromStr>::Err;
53
54 fn into_instance(self) -> Result<Instance, Self::Error> {
55 Instance::from_str(self)
56 }
57 }
58
59 impl AsInstance for Instance {
60 type Error = Infallible;
61
62 fn into_instance(self) -> Result<Instance, Self::Error> {
63 Ok(self)
64 }
65 }
66
67 impl GiteratedApiBuilder {
68 pub fn from_local(instance: impl AsInstance) -> Result<Self, anyhow::Error> {
69 Ok(Self {
70 our_instance: instance.into_instance()?,
71 our_private_key: None,
72 our_public_key: None,
73 target_instance: None,
74 })
75 }
76
77 pub fn from_local_for_other(
78 instance: impl AsInstance,
79 other: impl AsInstance,
80 ) -> Result<Self, anyhow::Error> {
81 Ok(Self {
82 our_instance: instance.into_instance()?,
83 our_private_key: None,
84 our_public_key: None,
85 target_instance: Some(other.into_instance()?),
86 })
87 }
88
89 pub fn private_key(&mut self, key: impl ToString) -> &mut Self {
90 self.our_private_key = Some(key.to_string());
91
92 self
93 }
94
95 pub fn public_key(&mut self, key: impl ToString) -> &mut Self {
96 self.our_public_key = Some(key.to_string());
97
98 self
99 }
100
101 pub async fn build(&mut self) -> Result<GiteratedApi, anyhow::Error> {
102 Ok(GiteratedApi::new(
103 self.our_instance.clone(),
104 self.our_private_key.clone().unwrap(),
105 self.our_public_key.clone().unwrap(),
106 self.target_instance.clone(),
107 )
108 .await?)
109 }
110 }
111
112 pub struct GiteratedApi {
113 pub connection: Socket,
114 pub our_instance: Instance,
115 pub our_private_key: String,
116 pub our_public_key: String,
117 pub target_instance: Option<Instance>,
118 pub target_public_key: Option<String>,
119 }
120
121 impl GiteratedApi {
122 pub async fn new(
123 local_instance: Instance,
124 private_key: String,
125 public_key: String,
126 target_instance: Option<Instance>,
127 ) -> Result<Self, anyhow::Error> {
128 let connection = Self::connect_to(
129 target_instance
130 .clone()
131 .unwrap_or_else(|| local_instance.clone()),
132 )
133 .await?;
134
135 let mut api = GiteratedApi {
136 connection,
137 our_instance: local_instance,
138 our_private_key: private_key,
139 our_public_key: public_key,
140 target_instance,
141 target_public_key: None,
142 };
143
144 // Handle handshake
145 api.handle_handshake().await?;
146
147 Ok(api)
148 }
149
150 pub async fn public_key(&mut self) -> String {
151 if let Some(public_key) = &self.target_public_key {
152 public_key.clone()
153 } else {
154 let key = reqwest::get(format!(
155 "https://{}/.giterated/pubkey.pem",
156 self.target_instance
157 .as_ref()
158 .unwrap_or_else(|| &self.our_instance)
159 .url
160 ))
161 .await
162 .unwrap()
163 .text()
164 .await
165 .unwrap();
166
167 self.target_public_key = Some(key.clone());
168
169 key
170 }
171 }
172
173 /// Register on an [`Instance`].
174 ///
175 /// # Authorization
176 /// - Must be made by the same instance its being sent to
177 pub async fn register(
178 &mut self,
179 username: String,
180 email: Option<String>,
181 password: String,
182 ) -> Result<RegisterAccountResponse, Box<dyn Error>> {
183 let message = InstanceAuthenticated::new(
184 RegisterAccountRequest {
185 username,
186 email,
187 password,
188 },
189 self.our_instance.clone(),
190 self.our_private_key.clone(),
191 )
192 .unwrap();
193
194 self.send_message(&MessageKind::Authentication(
195 AuthenticationMessage::Request(AuthenticationRequest::RegisterAccount(message)),
196 ))
197 .await?;
198
199 while let Ok(payload) = self.next_payload().await {
200 if let Ok(MessageKind::Authentication(AuthenticationMessage::Response(
201 AuthenticationResponse::RegisterAccount(response),
202 ))) = serde_json::from_slice(&payload)
203 {
204 return Ok(response);
205 }
206 }
207
208 unreachable!()
209 }
210
211 /// Create repository on the target instance.
212 pub async fn create_repository(
213 &mut self,
214 user_token: String,
215 name: String,
216 description: Option<String>,
217 visibility: RepositoryVisibility,
218 default_branch: String,
219 owner: User,
220 ) -> Result<bool, Box<dyn Error>> {
221 let target_respository = Repository {
222 owner: owner.clone(),
223 name: name.clone(),
224 instance: self
225 .target_instance
226 .as_ref()
227 .unwrap_or(&self.our_instance)
228 .clone(),
229 };
230
231 let request = CreateRepositoryRequest {
232 name,
233 description,
234 visibility,
235 default_branch,
236 owner,
237 };
238
239 let message =
240 UnvalidatedUserAuthenticated::new(request, user_token, self.our_private_key.clone())
241 .unwrap();
242
243 self.send_message(&MessageKind::Repository(RepositoryMessage {
244 target: target_respository,
245 command: RepositoryMessageKind::Request(RepositoryRequest::CreateRepository(message)),
246 }))
247 .await?;
248
249 while let Ok(payload) = self.next_payload().await {
250 if let Ok(MessageKind::Repository(RepositoryMessage {
251 command:
252 RepositoryMessageKind::Response(RepositoryResponse::CreateRepository(_response)),
253 ..
254 })) = serde_json::from_slice(&payload)
255 {
256 return Ok(true);
257 }
258 }
259
260 unreachable!()
261 }
262
263 pub async fn repository_info(
264 &mut self,
265 token: &str,
266 repository: Repository,
267 ) -> Result<RepositoryView, Box<dyn Error>> {
268 let message = UnvalidatedUserAuthenticated::new(
269 RepositoryInfoRequest {
270 repository: repository.clone(),
271 extra_metadata: true,
272 rev: None,
273 path: None,
274 },
275 token.to_string(),
276 self.our_private_key.clone(),
277 )
278 .unwrap();
279
280 self.send_message(&MessageKind::Repository(RepositoryMessage {
281 target: repository.clone(),
282 command: RepositoryMessageKind::Request(RepositoryRequest::RepositoryInfo(message)),
283 }))
284 .await?;
285
286 loop {
287 // while let Ok(payload) = Self::next_payload(&mut socket).await {
288 let payload = match self.next_payload().await {
289 Ok(payload) => payload,
290 Err(err) => {
291 error!("Error while fetching next payload: {:?}", err);
292 continue;
293 }
294 };
295
296 if let Ok(MessageKind::Repository(RepositoryMessage {
297 command:
298 RepositoryMessageKind::Response(RepositoryResponse::RepositoryInfo(response)),
299 ..
300 })) = serde_json::from_slice(&payload)
301 {
302 return Ok(response);
303 }
304 }
305 }
306
307 /// Requests an authentication token for the given login.
308 ///
309 /// # Authorization
310 /// This request can only be sent to the same instance from which
311 /// it is issued.
312 pub async fn authentication_token(
313 &mut self,
314 secret_key: String,
315 username: String,
316 password: String,
317 ) -> Result<String, Box<dyn Error>> {
318 let request = InstanceAuthenticated::new(
319 AuthenticationTokenRequest {
320 secret_key,
321 username,
322 password,
323 },
324 self.our_instance.clone(),
325 include_str!("example_keys/giterated.key").to_string(),
326 )
327 .unwrap();
328
329 self.send_message(&MessageKind::Authentication(
330 AuthenticationMessage::Request(AuthenticationRequest::AuthenticationToken(request)),
331 ))
332 .await?;
333
334 loop {
335 // while let Ok(payload) = Self::next_payload(&mut socket).await {
336 let payload = match self.next_payload().await {
337 Ok(payload) => payload,
338 Err(err) => {
339 error!("Error while fetching next payload: {:?}", err);
340 continue;
341 }
342 };
343
344 if let Ok(MessageKind::Authentication(AuthenticationMessage::Response(
345 AuthenticationResponse::AuthenticationToken(response),
346 ))) = serde_json::from_slice(&payload)
347 {
348 return Ok(response.token);
349 }
350 }
351 }
352
353 /// Requests a new token for the given login.
354 ///
355 /// # Authorization
356 /// This request can only be sent to the same instance from which
357 /// it is issued.
358 pub async fn extend_token(
359 &mut self,
360 secret_key: String,
361 token: String,
362 ) -> Result<Option<String>, Box<dyn Error>> {
363 let request = InstanceAuthenticated::new(
364 TokenExtensionRequest { secret_key, token },
365 self.our_instance.clone(),
366 self.our_private_key.clone(),
367 )
368 .unwrap();
369
370 self.send_message(&MessageKind::Authentication(
371 AuthenticationMessage::Request(AuthenticationRequest::TokenExtension(request)),
372 ))
373 .await?;
374
375 while let Ok(payload) = self.next_payload().await {
376 if let Ok(MessageKind::Authentication(AuthenticationMessage::Response(
377 AuthenticationResponse::TokenExtension(response),
378 ))) = serde_json::from_slice(&payload)
379 {
380 return Ok(response.new_token);
381 }
382 }
383
384 todo!()
385 }
386
387 async fn connect_to(instance: Instance) -> Result<Socket, anyhow::Error> {
388 let url = &instance.url;
389 info!(
390 "Connecting to {}",
391 format!("wss://{}/.giterated/daemon/", url)
392 );
393 let (websocket, _response) =
394 connect_async(&format!("wss://{}/.giterated/daemon/", url)).await?;
395 info!("Connection established with {}", url);
396
397 Ok(websocket)
398 }
399
400 async fn handle_handshake(&mut self) -> Result<(), anyhow::Error> {
401 // Send handshake initiation
402
403 self.send_message(&MessageKind::Handshake(HandshakeMessage::Initiate(
404 InitiateHandshake {
405 identity: self.our_instance.clone(),
406 version: version(),
407 },
408 )))
409 .await?;
410
411 while let Some(message) = self.connection.next().await {
412 let message = match message {
413 Ok(message) => message,
414 Err(err) => {
415 error!("Error reading message: {:?}", err);
416 continue;
417 }
418 };
419
420 let payload = match message {
421 Message::Text(text) => text.into_bytes(),
422 Message::Binary(bytes) => bytes,
423 Message::Ping(_) => continue,
424 Message::Pong(_) => continue,
425 Message::Close(_) => {
426 panic!()
427 }
428 _ => unreachable!(),
429 };
430
431 info!("Read payload: {}", std::str::from_utf8(&payload).unwrap());
432
433 let message = match serde_json::from_slice::<MessageKind>(&payload) {
434 Ok(message) => message,
435 Err(err) => {
436 error!("Error deserializing message: {:?}", err);
437 continue;
438 }
439 };
440
441 if let MessageKind::Handshake(handshake) = message {
442 match handshake {
443 HandshakeMessage::Initiate(_) => unimplemented!(),
444 HandshakeMessage::Response(response) => {
445
446 let message = if !validate_version(&response.version) {
447 error!(
448 "Version compatibility failure! Our Version: {}, Their Version: {}",
449 version(),
450 response.version
451 );
452
453 HandshakeFinalize { success: false }
454 } else {
455 info!("Connected with a compatible version");
456
457 HandshakeFinalize { success: true }
458 };
459 // Send HandshakeMessage::Finalize
460 self.send_message(&MessageKind::Handshake(HandshakeMessage::Finalize(
461 message
462 )))
463 .await?;
464 }
465 HandshakeMessage::Finalize(finalize) => {
466 if finalize.success {
467 return Ok(());
468 } else {
469 panic!()
470 }
471 }
472 }
473 }
474 }
475
476 Ok(())
477 }
478
479 async fn send_message<T: Serialize>(&mut self, message: &T) -> Result<(), anyhow::Error> {
480 self.connection
481 .send(Message::Binary(serde_json::to_vec(&message).unwrap()))
482 .await?;
483 Ok(())
484 }
485
486 async fn next_payload(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
487 while let Some(message) = self.connection.next().await {
488 let message = message?;
489
490 match message {
491 Message::Text(text) => return Ok(text.into_bytes()),
492 Message::Binary(bytes) => return Ok(bytes),
493 Message::Ping(_) => continue,
494 Message::Pong(_) => continue,
495 Message::Close(_) => {
496 panic!()
497 }
498 _ => unreachable!(),
499 }
500 }
501
502 unreachable!()
503 }
504 }
505