From f997c93c232f01997db0ae3b9438b548e6f755a3 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Fri, 23 Oct 2020 15:29:32 +0200 Subject: [PATCH] More work on the API implementation --- Cargo.toml | 3 +- schema | 2 +- src/access.rs | 12 ++-- src/api.rs | 14 +--- src/connection.rs | 164 +++++++++++----------------------------------- src/main.rs | 2 - 6 files changed, 48 insertions(+), 149 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efcf734..30c11cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,8 @@ uuid = { version = "0.8", features = ["serde", "v4"] } clap = "2.33" # TODO update this if bindgen breaks (again) -rsasl = "0.2.2" +#rsasl = "0.2.3" +rsasl = { path = "../../rsasl" } # rumqtt needs tokio which I'm trying to get away from paho-mqtt = { git = "https://github.com/dequbed/paho.mqtt.rust.git", branch = "master", features = ["build_bindgen"] } diff --git a/schema b/schema index 95fa6c5..25effe2 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 95fa6c5eabc8fded8b69ce8e3e87bc5d9c37c027 +Subproject commit 25effe262629c6f92773d4e6762f404b858fb09a diff --git a/src/access.rs b/src/access.rs index 10fbbac..8c29d31 100644 --- a/src/access.rs +++ b/src/access.rs @@ -136,8 +136,8 @@ impl PermissionsProvider { error!(self.log, "Failed to create 'roles' directory: {}, skipping!", e); return Ok(()) } else { - // Rust's stdlib considers the last element the file name so we have to put a dummy here for - // .set_filename() to work correctly + // Rust's stdlib considers the last element the file name even when it's a directory so + // we have to put a dummy here for .set_filename() to work correctly path.push("dummy"); self.dump_roles(txn, path.clone())?; path.pop(); @@ -157,8 +157,8 @@ impl PermissionsProvider { error!(self.log, "Failed to create 'perms' directory: {}, skipping!", e); return Ok(()) } else { - // Rust's stdlib considers the last element the file name so we have to put a dummy here for - // .set_filename() to work correctly + // Rust's stdlib considers the last element the file name even when it's a directory so + // we have to put a dummy here for .set_filename() to work correctly path.push("dummy"); self.dump_perms(txn, path.clone())?; path.pop(); @@ -178,8 +178,8 @@ impl PermissionsProvider { error!(self.log, "Failed to create 'users' directory: {}, skipping!", e); return Ok(()) } else { - // Rust's stdlib considers the last element the file name so we have to put a dummy here for - // .set_filename() to work correctly + // Rust's stdlib considers the last element the file name even when it's a directory so + // we have to put a dummy here for .set_filename() to work correctly path.push("dummy"); self.dump_users(txn, path.clone())?; path.pop(); diff --git a/src/api.rs b/src/api.rs index 23a8633..5474c09 100644 --- a/src/api.rs +++ b/src/api.rs @@ -14,19 +14,7 @@ use capnp_rpc::rpc_twoparty_capnp::Side; use capnp::capability::FromServer; pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> { - let mut message = capnp::message::Builder::new_default(); - let mut outer = message.init_root::(); - let mut api = outer.init_api(); - - let mapi = MachinesAPI {}; - api.set_machines(capnp_rpc::new_client(mapi)); - - let network = VatNetwork::new(socket.clone(), socket, Side::Server, Default::default()); - let rpc = RpcSystem::new(Box::new(network), None).map(|_| ()); - - rpc.await; - - Ok(()) + unimplemented!() } pub struct MachinesAPI; diff --git a/src/connection.rs b/src/connection.rs index 1424884..c28fa2c 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -8,131 +8,43 @@ use crate::api; pub use crate::schema::connection_capnp; -pub async fn handle_connection(log: Logger, mut stream: TcpStream) -> Result<()> { - let host = "localhost"; - let program = "Difluoroborane-0.1.0"; - let version = (0u32,1u32); +use capnp::capability::{Params, Results, Promise, FromServer}; - - let receive_options = capnp::message::ReaderOptions::default(); - { - let message = capnp_futures::serialize::read_message(&mut stream, receive_options).await.unwrap().unwrap(); - let m = message.get_root::().unwrap(); - - if m.has_greet() { - match m.which() { - Ok(connection_capnp::message::Which::Greet(Ok(r))) => { - println!("Host {} with program {} is saying hello. They speak API version {}.{}.", - r.get_host().unwrap(), - r.get_program().unwrap(), - r.get_major(), - r.get_minor()) - }, - _ => { - // We *JUST* checked that it's a greeting. This can not happen - unreachable!() - } - } - } - } - - { - let mut message = capnp::message::Builder::new_default(); - let greet_outer = message.init_root::(); - let mut greeting = greet_outer.init_greet(); - greeting.set_host(host); - greeting.set_program(program); - greeting.set_major(version.0); - greeting.set_minor(version.1); - - capnp_futures::serialize::write_message(&mut stream, message).await?; - } - { - let mut message = capnp::message::Builder::new_default(); - let outer = message.init_root::(); - let mut mechs = outer.init_auth().init_mechanisms(1); - mechs.set(0, "PLAIN"); - - capnp_futures::serialize::write_message(&mut stream, message).await?; - } - - { - let message = capnp_futures::serialize::read_message(&mut stream, receive_options).await.unwrap().unwrap(); - let m = message.get_root::().unwrap(); - - let mut auth_success = false; - - match m.which() { - Ok(connection_capnp::message::Which::Auth(Ok(r))) => { - if let Ok(w) = r.which() { - use crate::auth::auth_capnp::auth_message::*; - match w { - Request(Ok(r)) => { - let m = r.get_mechanism().unwrap(); - println!("Client wants to AUTH using {:?}", &m); - let mut sasl = auth::Auth::new(); - let mut sess = sasl.ctx.server_start(&m).unwrap(); - - use crate::auth::auth_capnp::request::initial_response::*; - match r.get_initial_response().which() { - Ok(Initial(Ok(r))) => { - debug!(log, "Client Auth with initial data"); - let mut message = capnp::message::Builder::new_default(); - let mut outer = message.init_root::().init_auth(); - - match sess.step(r) { - Ok(rsasl::Step::Done(b)) => { - auth_success = true; - debug!(log, "Authentication successful"); - let mut outcome= outer.init_outcome(); - - outcome.set_result(auth::auth_capnp::outcome::Result::Successful); - if !b.is_empty() { - let mut add_data = outcome.init_additional_data(); - add_data.set_additional(&b); - } - }, - Ok(rsasl::Step::NeedsMore(b)) => { - debug!(log, "Authentication needs more data"); - outer.set_response(&b); - } - Err(e) => { - warn!(log, "Authentication error: {}", e); - let mut outcome = outer.init_outcome(); - - // TODO: Distinguish errors - outcome.set_result(auth::auth_capnp::outcome::Result::Failed); - outcome.set_action(auth::auth_capnp::outcome::Action::Retry); - outcome.set_help_text(&format!("{}", e)); - } - } - - capnp_futures::serialize::write_message(&mut stream, message).await?; - } - _ => { - } - } - }, - _ => { - } - } - } else { - println!("Got unexpected message"); - } - }, - Ok(_) => { - println!("Got unexpected message"); - } - Err(e) => { - println!("Got error {:?}", e); - } - } - - if auth_success { - info!(log, "Handing off to API connection handler"); - api::handle_connection(log, stream).await; - } - } - - Ok(()) +/// Connection context +struct Connection { + stream: TcpStream, + user: Option, +} + + +use connection_capnp::bootstrap::*; +impl connection_capnp::bootstrap::Server for Connection { + fn auth(&mut self, + _: Params, + mut res: Results + ) -> Promise<(), capnp::Error> { + // Forbid mutltiple authentication for now + // TODO: When should we allow multiple auth and how do me make sure that does not leak + // priviledges (e.g. due to previously issues caps)? + if self.user.is_none() { + res.get().set_auth(capnp_rpc::new_client(auth::Auth::new())) + } + + Promise::ok(()) + } + + fn permissions(&mut self, + _: Params, + mut res: Results + ) -> Promise<(), capnp::Error> { + if let Some(user) = self.user { + + } + + Promise::ok(()) + } +} + +pub async fn handle_connection(log: Logger, mut stream: TcpStream) -> Result<()> { + unimplemented!() } diff --git a/src/main.rs b/src/main.rs index 64744c6..58024ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -302,8 +302,6 @@ fn main() -> Result<(), Error> { }); // Check each signal as it arrives - // signals is a futures-0.1 stream, compat() makes it a futures-0.3 (which we use) stream - // Now actually check if a connection was opened or a signal recv'd let handle_signals = signal.map(|r| { r.unwrap() }).into_stream(); let mut combined = stream::select(handle_signals, handle_sockets);