diff --git a/giterated-daemon/src/connection/authentication.rs b/giterated-daemon/src/connection/authentication.rs index 7f0f676..70f2c86 100644 --- a/giterated-daemon/src/connection/authentication.rs +++ b/giterated-daemon/src/connection/authentication.rs @@ -1,9 +1,15 @@ +use std::sync::Arc; + use anyhow::Error; use thiserror::Error; +use tokio::{fs::File, io::AsyncReadExt}; use crate::message::{AuthenticatedInstance, Message, MessageHandler, NetworkMessage, State}; -use giterated_models::messages::authentication::{ - AuthenticationTokenRequest, RegisterAccountRequest, TokenExtensionRequest, +use giterated_models::{ + messages::authentication::{ + AuthenticationTokenRequest, RegisterAccountRequest, TokenExtensionRequest, AuthenticationTokenResponse, + }, + model::authenticated::InstanceAuthenticator, }; use super::wrapper::ConnectionState; @@ -69,6 +75,38 @@ async fn authentication_token_request( Message(request): Message, instance: AuthenticatedInstance, ) -> Result<(), AuthenticationConnectionError> { + if request.instance != connection_state.instance { + // We need to perform the authentication request on behalf of + // the user. TODO: Oauth-style flow + let mut connections = connection_state.instance_connections.lock().await; + + let connection = connections.get_or_open(&request.instance).unwrap(); + + let private_key = { + let mut file = File::open(connection_state.config["giterated"]["keys"]["private"].as_str().unwrap()).await.unwrap(); + + let mut key = String::new(); + file.read_to_string(&mut key).await.unwrap(); + + key + }; + + let authenticator = InstanceAuthenticator { + instance: connection_state.instance.clone(), + private_key, + }; + + let response = giterated_api::request::request_local(request) + .authenticate(authenticator) + .execute_expect::(&connection) + .await.unwrap(); + drop(connection); + + connection_state.send(response).await.map_err(|e| AuthenticationConnectionError::Sending(e))?; + + return Ok(()); + } + let issued_for = instance.inner().clone(); let mut token_granter = connection_state.auth_granter.lock().await; @@ -121,3 +159,12 @@ pub enum AuthenticationConnectionError { #[error("error issuing token")] TokenIssuance(Error), } + +async fn private_key(path: &str) -> Vec { + let mut file = File::open(path).await.unwrap(); + + let mut key = vec![]; + file.read_to_end(&mut key).await.unwrap(); + + key +} diff --git a/giterated-daemon/src/connection/forwarded.rs b/giterated-daemon/src/connection/forwarded.rs index a7e1d5a..c53ce9c 100644 --- a/giterated-daemon/src/connection/forwarded.rs +++ b/giterated-daemon/src/connection/forwarded.rs @@ -55,6 +55,4 @@ pub async fn wrap_forwarded(pool: &DaemonConnectionPool, message: AuthenticatedP } } } - - todo!() } diff --git a/giterated-daemon/src/connection/wrapper.rs b/giterated-daemon/src/connection/wrapper.rs index 01f7588..b717548 100644 --- a/giterated-daemon/src/connection/wrapper.rs +++ b/giterated-daemon/src/connection/wrapper.rs @@ -17,6 +17,7 @@ use serde::Serialize; use tokio::{net::TcpStream, sync::Mutex}; use tokio_tungstenite::{tungstenite::Message, WebSocketStream}; +use toml::Table; use crate::{ authentication::AuthenticationTokenGranter, @@ -42,6 +43,7 @@ pub async fn connection_wrapper( addr: SocketAddr, instance: impl ToOwned, instance_connections: Arc>, + config: Table, ) { let connection_state = ConnectionState { socket: Arc::new(Mutex::new(socket)), @@ -54,6 +56,8 @@ pub async fn connection_wrapper( instance: instance.to_owned(), handshaked: Arc::new(AtomicBool::new(false)), key_cache: Arc::default(), + instance_connections: instance_connections.clone(), + config, }; let mut handshaked = false; @@ -179,6 +183,8 @@ pub struct ConnectionState { pub instance: Instance, pub handshaked: Arc, pub key_cache: Arc>, + pub instance_connections: Arc>, + pub config: Table, } impl ConnectionState { diff --git a/giterated-daemon/src/main.rs b/giterated-daemon/src/main.rs index 4e805f0..a3c4e75 100644 --- a/giterated-daemon/src/main.rs +++ b/giterated-daemon/src/main.rs @@ -26,15 +26,15 @@ extern crate tracing; #[tokio::main] async fn main() -> Result<(), Error> { tracing_subscriber::fmt::init(); - let mut listener = TcpListener::bind("0.0.0.0:7270").await?; - let connections: Arc> = Arc::default(); - let instance_connections: Arc> = Arc::default(); let config: Table = { let mut file = File::open("Giterated.toml").await?; let mut text = String::new(); file.read_to_string(&mut text).await?; text.parse()? }; + let mut listener = TcpListener::bind(config["giterated"]["bind"].as_str().unwrap()).await?; + let connections: Arc> = Arc::default(); + let instance_connections: Arc> = Arc::default(); let db_conn_options = PgConnectOptions::new() .host(config["postgres"]["host"].as_str().unwrap()) .port(config["postgres"]["port"].as_integer().unwrap() as u16) @@ -117,6 +117,7 @@ async fn main() -> Result<(), Error> { address, Instance::from_str("giterated.dev").unwrap(), instance_connections.clone(), + config.clone(), )), }; diff --git a/giterated-models/src/messages/authentication.rs b/giterated-models/src/messages/authentication.rs index 6ee8984..f2c488b 100644 --- a/giterated-models/src/messages/authentication.rs +++ b/giterated-models/src/messages/authentication.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::model::authenticated::UserAuthenticationToken; +use crate::model::{authenticated::UserAuthenticationToken, instance::Instance}; /// An account registration request. /// @@ -32,6 +32,7 @@ pub struct RegisterAccountResponse { /// - Decrypts user private key to issue to #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AuthenticationTokenRequest { + pub instance: Instance, pub username: String, pub password: String, } diff --git a/giterated-models/src/messages/repository.rs b/giterated-models/src/messages/repository.rs index a8902e9..0b2597d 100644 --- a/giterated-models/src/messages/repository.rs +++ b/giterated-models/src/messages/repository.rs @@ -149,7 +149,6 @@ pub struct RepositoryInfoRequest { pub path: Option, } - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct RepositorySettingsRequest { pub repository: Repository, diff --git a/giterated-models/src/model/authenticated.rs b/giterated-models/src/model/authenticated.rs index b540998..daf8b64 100644 --- a/giterated-models/src/model/authenticated.rs +++ b/giterated-models/src/model/authenticated.rs @@ -22,7 +22,7 @@ pub struct UserTokenMetadata { #[derive(Debug)] pub struct Authenticated { pub target_instance: Option, - pub source: Vec>, + pub source: Vec>, pub message_type: String, pub message: T, } @@ -109,7 +109,10 @@ impl Authenticated { } } - pub fn append_authentication(&mut self, authentication: P) { + pub fn append_authentication( + &mut self, + authentication: P, + ) { let message_payload = serde_json::to_vec(&self.message).unwrap(); info!( @@ -117,7 +120,8 @@ impl Authenticated { std::str::from_utf8(&message_payload).unwrap() ); - self.source.push(Box::new(authentication) as Box); + self.source + .push(Box::new(authentication) as Box); } pub fn into_payload(self) -> AuthenticatedPayload { @@ -143,16 +147,16 @@ impl AuthenticationSourceProvider for UserAuthenticator { } #[derive(Debug, Clone)] -pub struct InstanceAuthenticator<'a> { +pub struct InstanceAuthenticator { pub instance: Instance, - pub private_key: &'a str, + pub private_key: String, } -impl AuthenticationSourceProvider for InstanceAuthenticator<'_> { +impl AuthenticationSourceProvider for InstanceAuthenticator { fn authenticate(&self, payload: &Vec) -> AuthenticationSource { let mut rng = rand::thread_rng(); - let private_key = RsaPrivateKey::from_pkcs1_pem(self.private_key).unwrap(); + let private_key = RsaPrivateKey::from_pkcs1_pem(&self.private_key).unwrap(); let signing_key = SigningKey::::new(private_key); let signature = signing_key.sign_with_rng(&mut rng, &payload); diff --git a/giterated-models/src/model/repository.rs b/giterated-models/src/model/repository.rs index 55fc76a..b030580 100644 --- a/giterated-models/src/model/repository.rs +++ b/giterated-models/src/model/repository.rs @@ -174,7 +174,13 @@ impl From> for Commit { Self { oid: commit.id().to_string(), // This shouldn't ever fail, as we already know the object has an oid. - short_oid: commit.as_object().short_id().unwrap().as_str().unwrap().to_string(), + short_oid: commit + .as_object() + .short_id() + .unwrap() + .as_str() + .unwrap() + .to_string(), message: commit.message().map(|message| message.to_string()), author: commit.author().into(), committer: commit.committer().into(),