diff --git a/Cargo.toml b/Cargo.toml index 0cac301..3e0be60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,7 @@ serde = { version = "1", features = ["derive"]} serde_json = "1.0" tracing-subscriber = "0.3" rand = "*" -jsonwebtoken = { version = "*", features = ["use_pem"]} \ No newline at end of file +jsonwebtoken = { version = "*", features = ["use_pem"]} +chrono = { version = "0.4", features = [ "serde", "std" ] } +reqwest = { version = "0.11" } +anyhow = "*" \ No newline at end of file diff --git a/src/example_keys/ec-private.pem b/src/example_keys/ec-private.pem deleted file mode 100644 index f3953c7..0000000 --- a/src/example_keys/ec-private.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0O9owT0TiWTcyrlU -u2o60jQKJlHOkPqwILUj7eENJ3KhRANCAATkzNqqkAqUWsfAfxqtpvC1QYS4E5RZ -vwz/QDoPtg+eIkytDJe/7aRzOY4+JSgQ+RHMyT822/PdCg5tZc5UYpjf ------END PRIVATE KEY----- diff --git a/src/example_keys/giterated.key b/src/example_keys/giterated.key new file mode 100644 index 0000000..1370005 --- /dev/null +++ b/src/example_keys/giterated.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/src/example_keys/giterated.key.pub b/src/example_keys/giterated.key.pub new file mode 100644 index 0000000..960a231 --- /dev/null +++ b/src/example_keys/giterated.key.pub @@ -0,0 +1,13 @@ +-----BEGIN RSA PUBLIC KEY----- +MIICCgKCAgEArGLMTdU15wfXOX3SwbQTZnAEvMfWK7JBH3t0ZHYcnQRJQt2/OAvH +9h8WwQAjXcoS9wEvFfEJA2OWdzHWJSSnDD8M5eI2F8+cZeQ5bBk6YGxyYkVopkV/ +RxoitlFFXeSTksQO7VVG9qiw5lcsW+WRw9LJHSyviV8AXZY0GK3RHZwsQnEYIfmi +waW1+gUEi2ztoBTQjJXH/MkXdYOXqINzVPta/cfw7lOJHOh1X4WSkKErfCR0GW4P +hAdj3hQFHknta9Fm2SBmgEZt6P/RSprUuKuX7Ufr8mb5Vqv7HwCKapsFym7Dgvbe +y3M8nCdpWc3w1B/X4vW0cePZg6XX3VAXmMgB6o9WW23IwAaWMWGe1v/oXOUj7y2+ +BIzCTUbYdDruVZ9szpNvq9ddGqTGhpivKnpyAANe5e4ggppV4zZC4yKmVqDQO2MJ +h+Qr0mIkbOFFbytvAzIlKBWL+zeMtloOjvIU5F6AoWCr3W0EoJy4oDR0ZVtovcqE +TPtYuoFJ1Y5qlAIiC/J1TfKIRhB09WiuSzG5enEOH8thojulS46kIgJqKLMOvBi3 +JUf5EHqEXGjIILcb9plAI6D3pHV4rUNIv3KY0ci2PqMfmHjE3v/ELlu4YKKqfhn/ +fDMuYphZ41Ga0eaKRhV4ClwFWeI0hOVXhk6C01UZZct2ZbvayraTZCECAwEAAQ== +-----END RSA PUBLIC KEY----- diff --git a/src/example_keys/key b/src/example_keys/key deleted file mode 100644 index 1c41f94..0000000 --- a/src/example_keys/key +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY----- -MFECAQEwBQYDK2VwBCIEIPKWOYTsaHy3zjshOGPFNrwDiD5UDlxTXxmJtVTxquaY -gSEARmLzPuvZandELMEhy8QECpA6cW17ny1Zu/DIgKFXs/o= ------END PRIVATE KEY----- \ No newline at end of file diff --git a/src/example_keys/key.pub b/src/example_keys/key.pub deleted file mode 100644 index 0f1a14b..0000000 --- a/src/example_keys/key.pub +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEARmLzPuvZandELMEhy8QECpA6cW17ny1Zu/DIgKFXs/o= ------END PUBLIC KEY----- \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index c84c4d2..1b52437 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,25 @@ +use std::convert::Infallible; +use std::str::FromStr; use std::{error::Error, net::SocketAddr}; use futures_util::{SinkExt, StreamExt}; +use giterated_daemon::messages::authentication::{RegisterAccountRequest, RegisterAccountResponse}; +use giterated_daemon::messages::UnvalidatedUserAuthenticated; +use giterated_daemon::model::repository::RepositoryVisibility; +use giterated_daemon::model::user::User; +use giterated_daemon::{version, validate_version}; use giterated_daemon::{ handshake::{HandshakeFinalize, HandshakeMessage, InitiateHandshake}, messages::{ + authentication::{ + AuthenticationMessage, AuthenticationRequest, AuthenticationResponse, + AuthenticationTokenRequest, TokenExtensionRequest, + }, repository::{ CreateRepositoryRequest, RepositoryInfoRequest, RepositoryMessage, RepositoryMessageKind, RepositoryRequest, RepositoryResponse, }, - MessageKind, + InstanceAuthenticated, MessageKind, }, model::{ instance::Instance, @@ -24,38 +35,224 @@ type Socket = WebSocketStream>; #[macro_use] extern crate tracing; -pub struct GiteratedApi; +pub struct GiteratedApiBuilder { + our_instance: Instance, + our_private_key: Option, + our_public_key: Option, + target_instance: Option, +} + +pub trait AsInstance { + type Error: Error + Send + Sync + 'static; + + fn into_instance(self) -> Result; +} + +impl AsInstance for &str { + type Error = ::Err; + + fn into_instance(self) -> Result { + Instance::from_str(self) + } +} + +impl AsInstance for Instance { + type Error = Infallible; + + fn into_instance(self) -> Result { + Ok(self) + } +} + +impl GiteratedApiBuilder { + pub fn from_local(instance: impl AsInstance) -> Result { + Ok(Self { + our_instance: instance.into_instance()?, + our_private_key: None, + our_public_key: None, + target_instance: None, + }) + } + + pub fn from_local_for_other( + instance: impl AsInstance, + other: impl AsInstance, + ) -> Result { + Ok(Self { + our_instance: instance.into_instance()?, + our_private_key: None, + our_public_key: None, + target_instance: Some(other.into_instance()?), + }) + } + + pub fn private_key(&mut self, key: impl ToString) -> &mut Self { + self.our_private_key = Some(key.to_string()); + + self + } + + pub fn public_key(&mut self, key: impl ToString) -> &mut Self { + self.our_public_key = Some(key.to_string()); + + self + } + + pub async fn build(&mut self) -> Result { + Ok(GiteratedApi::new( + self.our_instance.clone(), + self.our_private_key.clone().unwrap(), + self.our_public_key.clone().unwrap(), + self.target_instance.clone(), + ) + .await?) + } +} + +pub struct GiteratedApi { + pub connection: Socket, + pub our_instance: Instance, + pub our_private_key: String, + pub our_public_key: String, + pub target_instance: Option, + pub target_public_key: Option, +} impl GiteratedApi { + pub async fn new( + local_instance: Instance, + private_key: String, + public_key: String, + target_instance: Option, + ) -> Result { + let connection = Self::connect_to( + target_instance + .clone() + .unwrap_or_else(|| local_instance.clone()), + ) + .await?; + + let mut api = GiteratedApi { + connection, + our_instance: local_instance, + our_private_key: private_key, + our_public_key: public_key, + target_instance, + target_public_key: None, + }; + + // Handle handshake + api.handle_handshake().await?; + + Ok(api) + } + + pub async fn public_key(&mut self) -> String { + if let Some(public_key) = &self.target_public_key { + public_key.clone() + } else { + let key = reqwest::get(format!( + "https://{}/.giterated/pubkey.pem", + self.target_instance + .as_ref() + .unwrap_or_else(|| &self.our_instance) + .url + )) + .await + .unwrap() + .text() + .await + .unwrap(); + + self.target_public_key = Some(key.clone()); + + key + } + } + + /// Register on an [`Instance`]. + /// + /// # Authorization + /// - Must be made by the same instance its being sent to + pub async fn register( + &mut self, + username: String, + email: Option, + password: String, + ) -> Result> { + let message = InstanceAuthenticated::new( + RegisterAccountRequest { + username, + email, + password, + }, + self.our_instance.clone(), + self.our_private_key.clone(), + ) + .unwrap(); + + self.send_message(&MessageKind::Authentication( + AuthenticationMessage::Request(AuthenticationRequest::RegisterAccount(message)), + )) + .await?; + + while let Ok(payload) = self.next_payload().await { + if let Ok(MessageKind::Authentication(AuthenticationMessage::Response( + AuthenticationResponse::RegisterAccount(response), + ))) = serde_json::from_slice(&payload) + { + return Ok(response); + } + } + + unreachable!() + } + + /// Create repository on the target instance. pub async fn create_repository( - target: Instance, - request: CreateRepositoryRequest, + &mut self, + user_token: String, + name: String, + description: Option, + visibility: RepositoryVisibility, + default_branch: String, + owner: User, ) -> Result> { - let mut socket = Self::connect_to(&target.url).await?; - let target = Repository { - name: request.name.clone(), - instance: target, + let target_respository = Repository { + owner: owner.clone(), + name: name.clone(), + instance: self + .target_instance + .as_ref() + .unwrap_or(&self.our_instance) + .clone(), }; - Self::send_message( - &MessageKind::Repository(RepositoryMessage { - target, - command: RepositoryMessageKind::Request(RepositoryRequest::CreateRepository( - request, - )), - }), - &mut socket, - ) + let request = CreateRepositoryRequest { + name, + description, + visibility, + default_branch, + owner, + }; + + let message = + UnvalidatedUserAuthenticated::new(request, user_token, self.our_private_key.clone()) + .unwrap(); + + self.send_message(&MessageKind::Repository(RepositoryMessage { + target: target_respository, + command: RepositoryMessageKind::Request(RepositoryRequest::CreateRepository(message)), + })) .await?; - while let Ok(payload) = Self::next_payload(&mut socket).await { + while let Ok(payload) = self.next_payload().await { if let Ok(MessageKind::Repository(RepositoryMessage { command: RepositoryMessageKind::Response(RepositoryResponse::CreateRepository(_response)), .. })) = serde_json::from_slice(&payload) { - socket.close(None).await?; return Ok(true); } } @@ -63,23 +260,32 @@ impl GiteratedApi { unreachable!() } - pub async fn repository_info(repository: Repository) -> Result> { - let mut socket = Self::connect_to(&repository.instance.url).await?; - - Self::send_message( - &MessageKind::Repository(RepositoryMessage { - target: repository.clone(), - command: RepositoryMessageKind::Request(RepositoryRequest::RepositoryInfo( - RepositoryInfoRequest, - )), - }), - &mut socket, + pub async fn repository_info( + &mut self, + token: &str, + repository: Repository, + ) -> Result> { + let message = UnvalidatedUserAuthenticated::new( + RepositoryInfoRequest { + repository: repository.clone(), + extra_metadata: true, + rev: None, + path: None, + }, + token.to_string(), + self.our_private_key.clone(), ) + .unwrap(); + + self.send_message(&MessageKind::Repository(RepositoryMessage { + target: repository.clone(), + command: RepositoryMessageKind::Request(RepositoryRequest::RepositoryInfo(message)), + })) .await?; loop { // while let Ok(payload) = Self::next_payload(&mut socket).await { - let payload = match Self::next_payload(&mut socket).await { + let payload = match self.next_payload().await { Ok(payload) => payload, Err(err) => { error!("Error while fetching next payload: {:?}", err); @@ -93,37 +299,116 @@ impl GiteratedApi { .. })) = serde_json::from_slice(&payload) { - socket.close(None).await?; return Ok(response); } } + } - unreachable!() + /// Requests an authentication token for the given login. + /// + /// # Authorization + /// This request can only be sent to the same instance from which + /// it is issued. + pub async fn authentication_token( + &mut self, + secret_key: String, + username: String, + password: String, + ) -> Result> { + let request = InstanceAuthenticated::new( + AuthenticationTokenRequest { + secret_key, + username, + password, + }, + self.our_instance.clone(), + include_str!("example_keys/giterated.key").to_string(), + ) + .unwrap(); + + self.send_message(&MessageKind::Authentication( + AuthenticationMessage::Request(AuthenticationRequest::AuthenticationToken(request)), + )) + .await?; + + loop { + // while let Ok(payload) = Self::next_payload(&mut socket).await { + let payload = match self.next_payload().await { + Ok(payload) => payload, + Err(err) => { + error!("Error while fetching next payload: {:?}", err); + continue; + } + }; + + if let Ok(MessageKind::Authentication(AuthenticationMessage::Response( + AuthenticationResponse::AuthenticationToken(response), + ))) = serde_json::from_slice(&payload) + { + return Ok(response.token); + } + } } - async fn connect_to(url: impl ToString) -> Result> { - let url = url.to_string(); - let (mut websocket, _response) = connect_async(&format!("ws://{}/.giterated/daemon", url)).await?; - Self::handle_handshake(&mut websocket).await?; + /// Requests a new token for the given login. + /// + /// # Authorization + /// This request can only be sent to the same instance from which + /// it is issued. + pub async fn extend_token( + &mut self, + secret_key: String, + token: String, + ) -> Result, Box> { + let request = InstanceAuthenticated::new( + TokenExtensionRequest { secret_key, token }, + self.our_instance.clone(), + self.our_private_key.clone(), + ) + .unwrap(); + + self.send_message(&MessageKind::Authentication( + AuthenticationMessage::Request(AuthenticationRequest::TokenExtension(request)), + )) + .await?; + + while let Ok(payload) = self.next_payload().await { + if let Ok(MessageKind::Authentication(AuthenticationMessage::Response( + AuthenticationResponse::TokenExtension(response), + ))) = serde_json::from_slice(&payload) + { + return Ok(response.new_token); + } + } + + todo!() + } + + async fn connect_to(instance: Instance) -> Result { + let url = &instance.url; + info!( + "Connecting to {}", + format!("wss://{}/.giterated/daemon/", url) + ); + let (websocket, _response) = + connect_async(&format!("wss://{}/.giterated/daemon/", url)).await?; + info!("Connection established with {}", url); Ok(websocket) } - async fn handle_handshake(socket: &mut Socket) -> Result<(), Box> { + async fn handle_handshake(&mut self) -> Result<(), anyhow::Error> { // Send handshake initiation - Self::send_message( - &MessageKind::Handshake(HandshakeMessage::Initiate(InitiateHandshake { - identity: Instance { - url: String::from("foo.com"), - }, - version: String::from("0.1.0"), - })), - socket, - ) + self.send_message(&MessageKind::Handshake(HandshakeMessage::Initiate( + InitiateHandshake { + identity: self.our_instance.clone(), + version: version(), + }, + ))) .await?; - while let Some(message) = socket.next().await { + while let Some(message) = self.connection.next().await { let message = match message { Ok(message) => message, Err(err) => { @@ -156,14 +441,25 @@ impl GiteratedApi { if let MessageKind::Handshake(handshake) = message { match handshake { HandshakeMessage::Initiate(_) => unimplemented!(), - HandshakeMessage::Response(_) => { + HandshakeMessage::Response(response) => { + + let message = if !validate_version(&response.version) { + error!( + "Version compatibility failure! Our Version: {}, Their Version: {}", + version(), + response.version + ); + + HandshakeFinalize { success: false } + } else { + info!("Connected with a compatible version"); + + HandshakeFinalize { success: true } + }; // Send HandshakeMessage::Finalize - Self::send_message( - &MessageKind::Handshake(HandshakeMessage::Finalize( - HandshakeFinalize { success: true }, - )), - socket, - ) + self.send_message(&MessageKind::Handshake(HandshakeMessage::Finalize( + message + ))) .await?; } HandshakeMessage::Finalize(finalize) => { @@ -180,18 +476,15 @@ impl GiteratedApi { Ok(()) } - async fn send_message( - message: &T, - socket: &mut Socket, - ) -> Result<(), Box> { - socket + async fn send_message(&mut self, message: &T) -> Result<(), anyhow::Error> { + self.connection .send(Message::Binary(serde_json::to_vec(&message).unwrap())) .await?; Ok(()) } - async fn next_payload(socket: &mut Socket) -> Result, Box> { - while let Some(message) = socket.next().await { + async fn next_payload(&mut self) -> Result, Box> { + while let Some(message) = self.connection.next().await { let message = message?; match message { diff --git a/src/main.rs b/src/main.rs index 651e4b5..a1565b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,17 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, str::FromStr, time::SystemTime}; -use giterated_api::GiteratedApi; -use giterated_daemon::model::{instance::Instance, repository::Repository}; -use jsonwebtoken::{encode, EncodingKey, Algorithm}; -use serde::{Serialize, Deserialize}; +use chrono::{DateTime, NaiveDateTime, Utc}; +use giterated_api::{GiteratedApi, GiteratedApiBuilder}; +use giterated_daemon::{ + messages::repository::CreateRepositoryRequest, + model::{ + instance::Instance, + repository::{Repository, RepositoryVisibility}, + user::User, + }, +}; +use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, TokenData, Validation}; +use serde::{Deserialize, Serialize}; // use jwt::SignWithKey; #[macro_use] @@ -23,21 +31,113 @@ async fn main() { // .await // ); - let encoding_key= EncodingKey::from_ec_pem(include_bytes!("example_keys/ec-private.pem")).unwrap(); + // let encoding_key = + // EncodingKey::from_rsa_pem(include_bytes!("example_keys/giterated.key")).unwrap(); - let claims = Claims { - instance: String::from("giterated.dev"), - username: String::from("ambee") - }; + // let claims = UserTokenMetadata { + // user: User { + // username: String::from("ambee"), + // instance: Instance { + // url: String::from("giterated.dev"), + // }, + // }, + // generated_for: Instance { + // url: String::from("giterated.dev"), + // }, + // exp: SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs(), + // }; + + // let token = encode( + // &jsonwebtoken::Header::new(Algorithm::RS256), + // &claims, + // &encoding_key, + // ) + // .unwrap(); + + let mut api = GiteratedApiBuilder::from_local("giterated.dev") + .unwrap() + .private_key(include_str!("example_keys/giterated.key")) + .public_key(include_str!("example_keys/giterated.key")) + .build() + .await + .unwrap(); + + info!("Lets try to make an account!"); + + let response = api + .register( + String::from("ambee"), + None, + String::from("lolthisisinthecommithistory"), + ) + .await; - let token = encode(&jsonwebtoken::Header::new(Algorithm::ES256), &claims, &encoding_key).unwrap(); + info!("Registration response: {:?}", response); + + let token = api + .authentication_token( + String::from("foobar"), + String::from("ambee"), + String::from("password"), + ) + .await + .unwrap(); println!("Token: {}", token); -} + let public_key = api.public_key().await; + + println!("Server public key:\n{}", public_key); + let verification_key = DecodingKey::from_rsa_pem(public_key.as_bytes()).unwrap(); + let data: TokenData = decode( + &token, + &verification_key, + &Validation::new(Algorithm::RS256), + ) + .unwrap(); + + println!("The token was valid! Data:\n{:#?}", data.claims); + + info!("Lets extend that token!"); + + let new_token = api + .extend_token(String::from("foobar"), token.clone()) + .await + .unwrap(); + info!("New Token Returned:\n{:?}", new_token); + + info!("Try to create a repository? uwu"); + + let repository = api + .create_repository( + new_token.unwrap(), + String::from("super-repository"), + None, + RepositoryVisibility::Public, + String::from("master"), + User::from_str("ambee:giterated.dev").unwrap(), + ) + .await + .unwrap(); + + assert!(repository); + + info!("Lets view our repository!"); + + let view = api + .repository_info( + &token, + Repository::from_str("ambee:giterated.dev/hello@giterated.dev").unwrap(), + ) + .await + .unwrap(); + + info!("Repository Info:\n{:#?}", view); +} #[derive(Debug, Serialize, Deserialize)] -struct Claims { - username: String, - instance: String -} \ No newline at end of file +struct UserTokenMetadata { + user: User, + generated_for: Instance, + exp: u64, +}