More work on the API implementation

This commit is contained in:
Gregor Reitzenstein 2020-10-23 15:29:32 +02:00
parent 87102b9f5d
commit f997c93c23
6 changed files with 48 additions and 149 deletions

View File

@ -36,7 +36,8 @@ uuid = { version = "0.8", features = ["serde", "v4"] }
clap = "2.33" clap = "2.33"
# TODO update this if bindgen breaks (again) # 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 # 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"] } paho-mqtt = { git = "https://github.com/dequbed/paho.mqtt.rust.git", branch = "master", features = ["build_bindgen"] }

2
schema

@ -1 +1 @@
Subproject commit 95fa6c5eabc8fded8b69ce8e3e87bc5d9c37c027 Subproject commit 25effe262629c6f92773d4e6762f404b858fb09a

View File

@ -136,8 +136,8 @@ impl PermissionsProvider {
error!(self.log, "Failed to create 'roles' directory: {}, skipping!", e); error!(self.log, "Failed to create 'roles' directory: {}, skipping!", e);
return Ok(()) return Ok(())
} else { } else {
// Rust's stdlib considers the last element the file name so we have to put a dummy here for // Rust's stdlib considers the last element the file name even when it's a directory so
// .set_filename() to work correctly // we have to put a dummy here for .set_filename() to work correctly
path.push("dummy"); path.push("dummy");
self.dump_roles(txn, path.clone())?; self.dump_roles(txn, path.clone())?;
path.pop(); path.pop();
@ -157,8 +157,8 @@ impl PermissionsProvider {
error!(self.log, "Failed to create 'perms' directory: {}, skipping!", e); error!(self.log, "Failed to create 'perms' directory: {}, skipping!", e);
return Ok(()) return Ok(())
} else { } else {
// Rust's stdlib considers the last element the file name so we have to put a dummy here for // Rust's stdlib considers the last element the file name even when it's a directory so
// .set_filename() to work correctly // we have to put a dummy here for .set_filename() to work correctly
path.push("dummy"); path.push("dummy");
self.dump_perms(txn, path.clone())?; self.dump_perms(txn, path.clone())?;
path.pop(); path.pop();
@ -178,8 +178,8 @@ impl PermissionsProvider {
error!(self.log, "Failed to create 'users' directory: {}, skipping!", e); error!(self.log, "Failed to create 'users' directory: {}, skipping!", e);
return Ok(()) return Ok(())
} else { } else {
// Rust's stdlib considers the last element the file name so we have to put a dummy here for // Rust's stdlib considers the last element the file name even when it's a directory so
// .set_filename() to work correctly // we have to put a dummy here for .set_filename() to work correctly
path.push("dummy"); path.push("dummy");
self.dump_users(txn, path.clone())?; self.dump_users(txn, path.clone())?;
path.pop(); path.pop();

View File

@ -14,19 +14,7 @@ use capnp_rpc::rpc_twoparty_capnp::Side;
use capnp::capability::FromServer; use capnp::capability::FromServer;
pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> { pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> {
let mut message = capnp::message::Builder::new_default(); unimplemented!()
let mut outer = message.init_root::<crate::connection::connection_capnp::message::Builder>();
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(())
} }
pub struct MachinesAPI; pub struct MachinesAPI;

View File

@ -8,131 +8,43 @@ use crate::api;
pub use crate::schema::connection_capnp; pub use crate::schema::connection_capnp;
pub async fn handle_connection(log: Logger, mut stream: TcpStream) -> Result<()> { use capnp::capability::{Params, Results, Promise, FromServer};
let host = "localhost";
let program = "Difluoroborane-0.1.0";
let version = (0u32,1u32);
/// Connection context
let receive_options = capnp::message::ReaderOptions::default(); struct Connection {
{ stream: TcpStream,
let message = capnp_futures::serialize::read_message(&mut stream, receive_options).await.unwrap().unwrap(); user: Option<auth::User>,
let m = message.get_root::<connection_capnp::message::Reader>().unwrap(); }
if m.has_greet() {
match m.which() { use connection_capnp::bootstrap::*;
Ok(connection_capnp::message::Which::Greet(Ok(r))) => { impl connection_capnp::bootstrap::Server for Connection {
println!("Host {} with program {} is saying hello. They speak API version {}.{}.", fn auth(&mut self,
r.get_host().unwrap(), _: Params<auth_params::Owned>,
r.get_program().unwrap(), mut res: Results<auth_results::Owned>
r.get_major(), ) -> Promise<(), capnp::Error> {
r.get_minor()) // 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)?
// We *JUST* checked that it's a greeting. This can not happen if self.user.is_none() {
unreachable!() res.get().set_auth(capnp_rpc::new_client(auth::Auth::new()))
} }
}
} Promise::ok(())
} }
{ fn permissions(&mut self,
let mut message = capnp::message::Builder::new_default(); _: Params<permissions_params::Owned>,
let greet_outer = message.init_root::<connection_capnp::message::Builder>(); mut res: Results<permissions_results::Owned>
let mut greeting = greet_outer.init_greet(); ) -> Promise<(), capnp::Error> {
greeting.set_host(host); if let Some(user) = self.user {
greeting.set_program(program);
greeting.set_major(version.0); }
greeting.set_minor(version.1);
Promise::ok(())
capnp_futures::serialize::write_message(&mut stream, message).await?; }
} }
{
let mut message = capnp::message::Builder::new_default(); pub async fn handle_connection(log: Logger, mut stream: TcpStream) -> Result<()> {
let outer = message.init_root::<connection_capnp::message::Builder>(); unimplemented!()
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::<connection_capnp::message::Reader>().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::<connection_capnp::message::Builder>().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(())
} }

View File

@ -302,8 +302,6 @@ fn main() -> Result<(), Error> {
}); });
// Check each signal as it arrives // 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 handle_signals = signal.map(|r| { r.unwrap() }).into_stream();
let mut combined = stream::select(handle_signals, handle_sockets); let mut combined = stream::select(handle_signals, handle_sockets);