From 4d2b0ea29c98c13fb2a9a156ca135e32c6b59aaa Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 18 Oct 2021 11:41:10 +0200 Subject: [PATCH 01/21] Update schema --- schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema b/schema index 1441b59..743c183 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 1441b59145bb364c2c61fe67f52c64a3522ee124 +Subproject commit 743c18393c6e01edeec1e04ab8998176d78f9a04 From 7bcb0712ae991b23fc4ddac8b24894695e8e05b7 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 20 Oct 2021 09:43:39 +0200 Subject: [PATCH 02/21] Implement wiki and URN links --- Cargo.lock | 2 +- src/api/machines.rs | 4 ++++ src/config.rs | 1 + src/machine.rs | 6 +++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fcd4eb..0aa81b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,7 +515,7 @@ dependencies = [ [[package]] name = "diflouroborane" -version = "0.2.0" +version = "0.3.0" dependencies = [ "async-channel", "async-trait", diff --git a/src/api/machines.rs b/src/api/machines.rs index 82edf71..0ba90e4 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -148,6 +148,10 @@ impl machines::Server for Machines { if let Some(ref desc) = machine.desc.description { builder.set_description(desc); } + if let Some(ref wiki) = machine.desc.wiki { + builder.set_wiki(wiki); + } + builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); let machineapi = Machine::new(user.clone(), perms, machine.clone()); let state = machine.get_status().await; diff --git a/src/config.rs b/src/config.rs index a820abc..1196b73 100644 --- a/src/config.rs +++ b/src/config.rs @@ -80,6 +80,7 @@ impl Default for Config { machines.insert("Testmachine".to_string(), MachineDescription { name: "Testmachine".to_string(), description: Some("A test machine".to_string()), + wiki: None, privs: PrivilegesBuf { disclose: PermissionBuf::from_string("lab.test.read".to_string()), read: PermissionBuf::from_string("lab.test.read".to_string()), diff --git a/src/machine.rs b/src/machine.rs index e585e4e..6c7b004 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -65,7 +65,7 @@ pub struct Machine { } impl Machine { - pub fn new(inner: Inner, desc: MachineDescription, ) -> Self { + pub fn new(inner: Inner, desc: MachineDescription) -> Self { Self { id: uuid::Uuid::default(), inner: Arc::new(Mutex::new(inner)), @@ -226,6 +226,10 @@ pub struct MachineDescription { /// An optional description of the Machine. pub description: Option, + #[serde(default)] + #[serde(flatten)] + pub wiki: Option, + /// The permission required #[serde(flatten)] pub privs: access::PrivilegesBuf, From 80df913089b5f41bdae850e882f4955ace74760b Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 25 Nov 2021 23:45:00 +0100 Subject: [PATCH 03/21] Also set URN and wiki links on machine lists --- src/api/machines.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/api/machines.rs b/src/api/machines.rs index 0ba90e4..95ebaf3 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -78,6 +78,10 @@ impl machines::Server for Machines { if let Some(ref desc) = machine.desc.description { builder.set_description(desc); } + if let Some(ref wiki) = machine.desc.wiki { + builder.set_wiki(wiki); + } + builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); let machineapi = Machine::new(user.clone(), perms, machine.clone()); From 76a1def45620c21eead49162df255667013b5893 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Fri, 26 Nov 2021 00:08:24 +0100 Subject: [PATCH 04/21] Implement getting a machine by URN --- src/api/machines.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/api/machines.rs b/src/api/machines.rs index 95ebaf3..d251ba5 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -199,4 +199,90 @@ impl machines::Server for Machines { Promise::ok(()) } } + + fn get_machine_u_r_n( + &mut self, + params: machines::GetMachineURNParams, + mut results: machines::GetMachineURNResults + ) -> Promise<(), capnp::Error> { + let rc = Rc::clone(&self.session); + if self.session.borrow().is_some() { + let urn = { + let params = pry!(params.get()); + pry!(params.get_urn()) + }; + + let mut parts = urn.split_terminator(':'); + let part_urn = parts.next().map(|u| u == "urn").unwrap_or(false); + let part_fabaccess = parts.next().map(|f| f == "fabaccess").unwrap_or(false); + let part_resource = parts.next().map(|r| r == "resource").unwrap_or(false); + if !(part_urn && part_fabaccess && part_resource) { + return Promise::ok(()) + } + + if let Some(name) = parts.next() { + let name = name.to_string(); + let network = self.network.clone(); + let f = async move { + let session = rc.borrow(); + let user = &session.as_ref().unwrap().authzid; + let permissions = &session.as_ref().unwrap().perms; + + if let Some(machine) = network.machines.get(&name) { + let mut builder = results.get().init_machine(); + let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); + builder.set_name(&name); + if let Some(ref desc) = machine.desc.description { + builder.set_description(desc); + } + if let Some(ref wiki) = machine.desc.wiki { + builder.set_wiki(wiki); + } + builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); + + let machineapi = Machine::new(user.clone(), perms, machine.clone()); + let state = machine.get_status().await; + if perms.write && state == Status::Free { + builder.set_use(capnp_rpc::new_client(machineapi.clone())); + } + if perms.manage { + //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); + //builder.set_check(capnp_rpc::new_client(machineapi.clone())); + builder.set_manage(capnp_rpc::new_client(machineapi.clone())); + } + if permissions.iter().any(|r| r.match_perm(&admin_perm())) { + builder.set_admin(capnp_rpc::new_client(machineapi.clone())); + } + + + let s = match machine.get_status().await { + Status::Free => MachineState::Free, + Status::Disabled => MachineState::Disabled, + Status::Blocked(_) => MachineState::Blocked, + Status::InUse(u) => { + if let Some(owner) = u.as_ref() { + if owner == user { + builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); + } + } + MachineState::InUse + }, + Status::Reserved(_) => MachineState::Reserved, + Status::ToCheck(_) => MachineState::ToCheck, + }; + builder.set_state(s); + + builder.set_info(capnp_rpc::new_client(machineapi)); + }; + + Ok(()) + }; + Promise::from_future(f) + } else { + Promise::ok(()) + } + } else { + Promise::ok(()) + } + } } From ab3ac8f730f836fdec9130133477bfef35bcd8d4 Mon Sep 17 00:00:00 2001 From: Kai Kriegel Date: Wed, 24 Nov 2021 20:35:54 +0000 Subject: [PATCH 05/21] added first start logic to seed db and keep state --- Dockerfile | 3 ++- docker/startup.sh | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docker/startup.sh diff --git a/Dockerfile b/Dockerfile index 0189219..51eccf0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,10 +15,11 @@ FROM debian:buster-slim RUN apt-get update && apt-get upgrade -yqq RUN apt-get install -yqq libgsasl7 && rm -rf /var/lib/apt/lists/* COPY --from=builder /usr/local/cargo/bin/diflouroborane /usr/local/bin/diflouroborane +COPY ./docker/startup.sh /startup.sh #COPY --from=builder /usr/src/bffh/examples/bffh.dhall /etc/diflouroborane.dhall # RUN diflouroborane --print-default > /etc/diflouroborane.toml VOLUME /etc/bffh/ VOLUME /var/lib/bffh/ VOLUME /usr/local/lib/bffh/adapters/ EXPOSE 59661 -ENTRYPOINT ["sh", "-c", "diflouroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh; diflouroborane -c /etc/bffh/bffh.dhall"] +ENTRYPOINT ["/bin/bash", "/startup.sh"] diff --git a/docker/startup.sh b/docker/startup.sh new file mode 100644 index 0000000..9aef043 --- /dev/null +++ b/docker/startup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +CONTAINER_ALREADY_STARTED="/var/lib/bffh/firststartflag" +if [ ! -e $CONTAINER_ALREADY_STARTED ]; then + touch $CONTAINER_ALREADY_STARTED + echo "-- Seeding Database --" + diflouroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh +else + diflouroborane -c /etc/bffh/bffh.dhall +fi From 200179f621511522265c2cd3e5c459314e84fcf7 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Fri, 26 Nov 2021 00:31:25 +0100 Subject: [PATCH 06/21] Returns current user --- src/api/machine.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/api/machine.rs b/src/api/machine.rs index 256a492..d0a8e72 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -63,17 +63,15 @@ impl info::Server for Machine { fn get_machine_info_extended( &mut self, _: info::GetMachineInfoExtendedParams, - _results: info::GetMachineInfoExtendedResults, + mut results: info::GetMachineInfoExtendedResults, ) -> Promise<(), capnp::Error> { - /*if self.perms.manage { + if self.perms.manage { let mut builder = results.get(); let mut extinfo = builder.init_machine_info_extended(); let mut current = extinfo.init_current_user(); - // FIXME fill user + current.set_username(&self.userid.uid); } - Promise::ok(())*/ - - Promise::err(capnp::Error::unimplemented("Extended Infos are unavailable".to_string())) + Promise::ok(()) } fn get_reservation_list( From 9fcb7664aa34c6abed166a7b690121fefb4d098d Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Fri, 26 Nov 2021 03:42:30 +0100 Subject: [PATCH 07/21] Also catch SIGQUIT and SIGTERM and clean up properly --- src/log.rs | 8 +++++--- src/main.rs | 6 +++--- src/server.rs | 6 +++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/log.rs b/src/log.rs index 0b0715b..c664e1d 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,11 +1,13 @@ use slog::{Drain, Logger}; use slog_async; +use slog_async::AsyncGuard; use slog_term::{TermDecorator, FullFormat}; -pub fn init() -> Logger { +pub fn init() -> (Logger, AsyncGuard) { let decorator = TermDecorator::new().build(); let drain = FullFormat::new(decorator).build().fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); + let (drain, guard) = slog_async::Async::new(drain).build_with_guard(); + let drain = drain.fuse(); - return slog::Logger::root(drain, o!()); + return (slog::Logger::root(drain, o!()), guard); } diff --git a/src/main.rs b/src/main.rs index 3e7df9b..5d73524 100644 --- a/src/main.rs +++ b/src/main.rs @@ -115,7 +115,8 @@ fn main() { // on. // TODO: Now would be a really good time to close stdin/out and move logging to syslog // Log is in an Arc so we can do very cheap clones in closures. - let log = Arc::new(log::init()); + let (log, guard) = log::init(); + let log = Arc::new(log); info!(log, "Starting"); match maybe(matches, log.clone()) { @@ -125,9 +126,8 @@ fn main() { retval = -1; } } + drop(guard); } - - std::process::exit(retval); } // Returning a `Result` from `main` allows us to use the `?` shorthand. diff --git a/src/server.rs b/src/server.rs index a50ef99..c51e8fb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,11 +27,15 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw -> Result<(), Error> { let signal = Box::pin(async { + use signal_hook::consts::signal::*; let (tx, mut rx) = UnixStream::pair()?; // Initialize signal handler. // We currently only care about Ctrl-C so SIGINT it is. // TODO: Make this do SIGHUP and a few others too. (By cloning the tx end of the pipe) - sigpipe::register(signal_hook::consts::SIGINT, tx.as_raw_fd())?; + let fd = tx.as_raw_fd(); + sigpipe::register(SIGINT, fd)?; + sigpipe::register(SIGQUIT, fd)?; + sigpipe::register(SIGTERM, fd)?; // When a signal is received this future can complete and read a byte from the underlying // socket — the actual data is discarded but the act of being able to receive data tells us // that we received a SIGINT. From 47781b445e474ebd3ab45754aab2713cfaaed297 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 1 Dec 2021 15:46:52 +0100 Subject: [PATCH 08/21] Make machine correctly load state and set previous/current use in API --- Cargo.toml | 2 +- src/api/machine.rs | 53 ++++++++++++++++++++++++---- src/db.rs | 4 ++- src/db/machine.rs | 2 +- src/db/machine/internal.rs | 21 +++++++---- src/machine.rs | 72 +++++++++++++++++++++++++++++--------- src/main.rs | 11 +++++- 7 files changed, 131 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a2747b1..c588f69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diflouroborane" -version = "0.3.0" +version = "0.3.1" authors = [ "dequbed " , "Kai Jan Kriegel " , "Joseph Langosch " diff --git a/src/api/machine.rs b/src/api/machine.rs index d0a8e72..65eb8d0 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -65,13 +65,52 @@ impl info::Server for Machine { _: info::GetMachineInfoExtendedParams, mut results: info::GetMachineInfoExtendedResults, ) -> Promise<(), capnp::Error> { - if self.perms.manage { - let mut builder = results.get(); - let mut extinfo = builder.init_machine_info_extended(); - let mut current = extinfo.init_current_user(); - current.set_username(&self.userid.uid); - } - Promise::ok(()) + let machine = self.machine.get_inner(); + let perms = self.perms.clone(); + let f = async move { + if perms.manage { + let mut builder = results.get(); + let mut extinfo = builder.init_machine_info_extended(); + let guard = machine.lock().await; + + // "previous" user + if let Some(user) = guard.get_previous() { + let mut previous = extinfo.reborrow().init_transfer_user(); + previous.set_username(&user.uid); + } + + let state = guard.read_state(); + let state_lock = state.lock_ref(); + match state_lock.state { + Status::Free => {} + Status::InUse(ref user) => if user.is_some() { + let user = user.as_ref().unwrap(); + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::ToCheck(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::Blocked(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::Disabled => {} + Status::Reserved(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + } + } + + Ok(()) + }; + + let g = smol::future::race(f, smol::Timer::after(Duration::from_secs(4)) + .map(|_| Err(capnp::Error::failed("Waiting for machine lock timed out!".to_string())))); + + Promise::from_future(g) } fn get_reservation_list( diff --git a/src/db.rs b/src/db.rs index d4c1305..be802e6 100644 --- a/src/db.rs +++ b/src/db.rs @@ -24,10 +24,12 @@ pub mod access; /// Stores&Retrieves Machines pub mod machine; +pub type MachineDB = machine::internal::Internal; + #[derive(Clone)] pub struct Databases { pub access: Arc, - pub machine: Arc, + pub machine: Arc, pub userdb: Arc, } diff --git a/src/db/machine.rs b/src/db/machine.rs index 01b8002..7e371c4 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -75,7 +75,7 @@ impl MachineState { pub fn init(log: Logger, _config: &Config, env: Arc) -> Result { let mut flags = lmdb::DatabaseFlags::empty(); - flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); + //flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let machdb = env.create_db(Some("machines"), flags)?; debug!(&log, "Opened machine db successfully."); diff --git a/src/db/machine/internal.rs b/src/db/machine/internal.rs index 9ae80be..99f36fa 100644 --- a/src/db/machine/internal.rs +++ b/src/db/machine/internal.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use slog::Logger; -use lmdb::{Environment, Transaction, RwTransaction, Cursor}; +use lmdb::{Environment, Transaction, RwTransaction, Cursor, RoTransaction}; use super::{MachineIdentifier, MachineState}; use crate::error::Result; @@ -37,11 +37,11 @@ impl Internal { self.get_with_txn(&txn, id) } - pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &String, status: &MachineState) + pub fn put_with_txn(&self, txn: &mut RwTransaction, id: &String, status: &MachineState) -> Result<()> { let bytes = flexbuffers::to_vec(status)?; - txn.put(self.db, &uuid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; + txn.put(self.db, &id.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; Ok(()) } @@ -52,11 +52,20 @@ impl Internal { txn.commit().map_err(Into::into) } - pub fn iter(&self, txn: &T) -> Result> { + pub fn iter<'txn, T: Transaction>(&self, txn: &'txn T) + -> Result> + { let mut cursor = txn.open_ro_cursor(self.db)?; Ok(cursor.iter_start().map(|buf| { - let (_kbuf, vbuf) = buf.unwrap(); - flexbuffers::from_slice(vbuf).unwrap() + let (kbuf, vbuf) = buf.unwrap(); + let id = unsafe { std::str::from_utf8_unchecked(kbuf) }; + let state = flexbuffers::from_slice(vbuf).unwrap(); + (id, state) })) } + + pub fn txn(&self) -> Result { + let txn = self.env.begin_ro_txn()?; + Ok(txn) + } } diff --git a/src/machine.rs b/src/machine.rs index 6c7b004..18f9afe 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -19,12 +19,13 @@ use futures::channel::{mpsc, oneshot}; use futures_signals::signal::Signal; use futures_signals::signal::SignalExt; use futures_signals::signal::{Mutable, ReadOnlyMutable}; +use slog::Logger; use crate::error::{Result, Error}; -use crate::db::access; +use crate::db::{access, Databases, MachineDB}; use crate::db::machine::{MachineIdentifier, MachineState, Status}; -use crate::db::user::{User, UserData}; +use crate::db::user::{User, UserData, UserId}; use crate::network::MachineMap; use crate::space; @@ -73,13 +74,14 @@ impl Machine { } } - pub fn construct - ( id: MachineIdentifier - , desc: MachineDescription - , state: MachineState - ) -> Machine + pub fn construct( + id: MachineIdentifier, + desc: MachineDescription, + state: MachineState, + db: Arc, + ) -> Machine { - Self::new(Inner::new(id, state), desc) + Self::new(Inner::new(id, state, db), desc) } pub fn do_state_change(&self, new_state: MachineState) @@ -136,17 +138,20 @@ pub struct Inner { /// case of an actor it should then make sure that the real world matches up with the set state state: Mutable, reset: Option, + + previous: Option, + + db: Arc, } impl Inner { - pub fn new ( id: MachineIdentifier - , state: MachineState - ) -> Inner - { + pub fn new(id: MachineIdentifier, state: MachineState, db: Arc) -> Inner { Inner { id, state: Mutable::new(state), reset: None, + previous: None, + db, } } @@ -162,8 +167,13 @@ impl Inner { Box::pin(self.state.signal_cloned().dedupe_cloned()) } + fn replace_state(&mut self, new_state: MachineState) -> MachineState { + self.db.put(&self.id, &new_state); + self.state.replace(new_state) + } + pub fn do_state_change(&mut self, new_state: MachineState) { - let old_state = self.state.replace(new_state); + let old_state = self.replace_state(new_state); self.reset.replace(old_state); } @@ -176,9 +186,30 @@ impl Inner { } pub fn reset_state(&mut self) { - if let Some(state) = self.reset.take() { - self.state.replace(state); + let previous_state = self.read_state(); + let state_lock = previous_state.lock_ref(); + // Only update previous user if state changed from InUse or ToCheck to whatever. + match state_lock.state { + Status::InUse(ref user) => { + self.previous = user.clone(); + }, + Status::ToCheck(ref user) => { + self.previous = Some(user.clone()); + }, + _ => {}, } + drop(state_lock); + + if let Some(state) = self.reset.take() { + self.replace_state(state); + } else { + // Default to Free + self.replace_state(MachineState::free()); + } + } + + pub fn get_previous(&self) -> &Option { + &self.previous } } @@ -242,15 +273,22 @@ impl MachineDescription { } } -pub fn load(config: &crate::config::Config) +pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) -> Result { let mut map = config.machines.clone(); + let db = db.machine; let it = map.drain() .map(|(k,v)| { // TODO: Read state from the state db - (v.name.clone(), Machine::construct(k, v, MachineState::new())) + if let Some(state) = db.get(&k).unwrap() { + debug!(log, "Loading old state from db for {}: {:?}", &k, &state); + (v.name.clone(), Machine::construct(k, v, state, db.clone())) + } else { + debug!(log, "No old state found in db for {}, creating new.", &k); + (v.name.clone(), Machine::construct(k, v, MachineState::new(), db.clone())) + } }); diff --git a/src/main.rs b/src/main.rs index 5d73524..5543f88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -167,7 +167,16 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { let ex = Executor::new(); let db = db::Databases::new(&log, &config)?; - let machines = machine::load(&config)?; + { + info!(log, "Loaded DB state:"); + let txn = db.machine.txn()?; + for (id, state) in db.machine.iter(&txn)? { + info!(log, "- {}: {:?}", id, state); + } + info!(log, "Loaded DB state END."); + } + + let machines = machine::load(&config, db.clone(), &log)?; let (actor_map, actors) = actor::load(&log, &config)?; let (init_map, initiators) = initiator::load(&log, &config)?; From 2a57ce2c28770b95bce1e64134e9601fe6783800 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 1 Dec 2021 15:47:28 +0100 Subject: [PATCH 09/21] Lock! --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0aa81b6..ec8c80a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,7 +515,7 @@ dependencies = [ [[package]] name = "diflouroborane" -version = "0.3.0" +version = "0.3.1" dependencies = [ "async-channel", "async-trait", From fd7fec2cbe1b64302ba8ff9c4aad29da616b6fa1 Mon Sep 17 00:00:00 2001 From: Kai Kriegel Date: Wed, 24 Nov 2021 20:35:54 +0000 Subject: [PATCH 10/21] Revert "added first start logic to seed db and keep state" This reverts commit ab3ac8f730f836fdec9130133477bfef35bcd8d4. --- Dockerfile | 3 +-- docker/startup.sh | 10 ---------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 docker/startup.sh diff --git a/Dockerfile b/Dockerfile index 51eccf0..0189219 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,11 +15,10 @@ FROM debian:buster-slim RUN apt-get update && apt-get upgrade -yqq RUN apt-get install -yqq libgsasl7 && rm -rf /var/lib/apt/lists/* COPY --from=builder /usr/local/cargo/bin/diflouroborane /usr/local/bin/diflouroborane -COPY ./docker/startup.sh /startup.sh #COPY --from=builder /usr/src/bffh/examples/bffh.dhall /etc/diflouroborane.dhall # RUN diflouroborane --print-default > /etc/diflouroborane.toml VOLUME /etc/bffh/ VOLUME /var/lib/bffh/ VOLUME /usr/local/lib/bffh/adapters/ EXPOSE 59661 -ENTRYPOINT ["/bin/bash", "/startup.sh"] +ENTRYPOINT ["sh", "-c", "diflouroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh; diflouroborane -c /etc/bffh/bffh.dhall"] diff --git a/docker/startup.sh b/docker/startup.sh deleted file mode 100644 index 9aef043..0000000 --- a/docker/startup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -CONTAINER_ALREADY_STARTED="/var/lib/bffh/firststartflag" -if [ ! -e $CONTAINER_ALREADY_STARTED ]; then - touch $CONTAINER_ALREADY_STARTED - echo "-- Seeding Database --" - diflouroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh -else - diflouroborane -c /etc/bffh/bffh.dhall -fi From eeb0ff306b4518b45a26ca45b1b9519d0a77c661 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 5 Dec 2021 18:53:02 +0100 Subject: [PATCH 11/21] Fix Machines using their name instead of their id --- examples/bffh.dhall | 2 +- schema | 2 +- src/api/machines.rs | 14 ++++++++------ src/machine.rs | 12 ++++++------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index 308f19f..e85c3ac 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -35,7 +35,7 @@ { description = Some "A test machine" , disclose = "lab.test.read" , manage = "lab.test.admin" - , name = "Testmachine" + , name = "MachineA" , read = "lab.test.read" , write = "lab.test.write" }, diff --git a/schema b/schema index 743c183..c855646 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 743c18393c6e01edeec1e04ab8998176d78f9a04 +Subproject commit c855646a90958ae575d58be074d187acb9f8f4fa diff --git a/src/api/machines.rs b/src/api/machines.rs index d251ba5..43ae558 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -74,7 +74,8 @@ impl machines::Server for Machines { let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); let mut builder = machines.reborrow().get(i as u32); - builder.set_name(&name); + builder.set_id(&name); + builder.set_name(&machine.desc.name); if let Some(ref desc) = machine.desc.description { builder.set_description(desc); } @@ -134,9 +135,9 @@ impl machines::Server for Machines { ) -> Promise<(), capnp::Error> { let rc = Rc::clone(&self.session); if self.session.borrow().is_some() { - let name = { + let id = { let params = pry!(params.get()); - pry!(params.get_name()).to_string() + pry!(params.get_id()).to_string() }; let network = self.network.clone(); @@ -145,17 +146,18 @@ impl machines::Server for Machines { let user = &session.as_ref().unwrap().authzid; let permissions = &session.as_ref().unwrap().perms; - if let Some(machine) = network.machines.get(&name) { + if let Some(machine) = network.machines.get(&id) { let mut builder = results.get().init_machine(); let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - builder.set_name(&name); + builder.set_id(&id); + builder.set_name(&machine.desc.name); if let Some(ref desc) = machine.desc.description { builder.set_description(desc); } if let Some(ref wiki) = machine.desc.wiki { builder.set_wiki(wiki); } - builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); + builder.set_urn(&format!("urn:fabaccess:resource:{}", &id)); let machineapi = Machine::new(user.clone(), perms, machine.clone()); let state = machine.get_status().await; diff --git a/src/machine.rs b/src/machine.rs index 18f9afe..da2ed0f 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -59,16 +59,16 @@ impl Index { // memory #[derive(Debug, Clone)] pub struct Machine { - pub id: uuid::Uuid, + pub id: MachineIdentifier, pub desc: MachineDescription, inner: Arc>, } impl Machine { - pub fn new(inner: Inner, desc: MachineDescription) -> Self { + pub fn new(inner: Inner, id: MachineIdentifier, desc: MachineDescription) -> Self { Self { - id: uuid::Uuid::default(), + id, inner: Arc::new(Mutex::new(inner)), desc, } @@ -81,7 +81,7 @@ impl Machine { db: Arc, ) -> Machine { - Self::new(Inner::new(id, state, db), desc) + Self::new(Inner::new(id.clone(), state, db), id, desc) } pub fn do_state_change(&self, new_state: MachineState) @@ -284,10 +284,10 @@ pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) // TODO: Read state from the state db if let Some(state) = db.get(&k).unwrap() { debug!(log, "Loading old state from db for {}: {:?}", &k, &state); - (v.name.clone(), Machine::construct(k, v, state, db.clone())) + (k.clone(), Machine::construct(k, v, state, db.clone())) } else { debug!(log, "No old state found in db for {}, creating new.", &k); - (v.name.clone(), Machine::construct(k, v, MachineState::new(), db.clone())) + (k.clone(), Machine::construct(k, v, MachineState::new(), db.clone())) } }); From 6d3e08955ade543bf6e573387a6942eb9a24bc43 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 5 Dec 2021 19:23:35 +0100 Subject: [PATCH 12/21] Config improvements and make shellies have a topic parameter --- examples/bffh.dhall | 19 ++++++++++--------- src/actor.rs | 2 +- src/config.rs | 22 ++++++++++++++++++---- src/main.rs | 14 +++++++------- src/modules/shelly.rs | 21 ++++++++++++++++----- 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index e85c3ac..fd33768 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -1,14 +1,15 @@ -- { actor_connections = [] : List { _1 : Text, _2 : Text } { actor_connections = -- Link up machines to actors - [ { _1 = "Testmachine", _2 = "Shelly_1234" } - , { _1 = "Another", _2 = "Bash" } + [ { machine = "Testmachine", actor = "Shelly1234" } + , { machine = "Another", actor = "Bash" } -- One machine can have as many actors as it wants - , { _1 = "Yetmore", _2 = "Bash2" } - , { _1 = "Yetmore", _2 = "FailBash"} + , { machine = "Yetmore", actor = "Bash2" } + , { machine = "Yetmore", actor = "FailBash"} ] , actors = - { Shelly_1234 = { module = "Shelly", params = {=} } + { Shelly_1234 = { module = "Shelly", params = + { topic = "Topic1234" }} , Bash = { module = "Process", params = { cmd = "./examples/actor.sh" , args = "your ad could be here" @@ -21,10 +22,10 @@ { cmd = "./examples/fail-actor.sh" }} } - , init_connections = [] : List { _1 : Text, _2 : Text } ---, init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }] -, initiators = {=} - --{ Initiator = { module = "Dummy", params = {=} } } + --, init_connections = [] : List { machine : Text, initiator : Text } +, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] +, initiators = --{=} + { Initiator = { module = "Dummy", params = {=} } } , listens = [ { address = "127.0.0.1", port = Some 59661 } , { address = "::1", port = Some 59661 } diff --git a/src/actor.rs b/src/actor.rs index a8efaec..ec29c36 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -172,7 +172,7 @@ fn load_single( .map(|a| a.into_boxed_actuator()) } "Shelly" => { - Some(Box::new(Shelly::new(log, name.clone(), client))) + Some(Box::new(Shelly::new(log, name.clone(), client, params))) } _ => { error!(log, "No actor found with name \"{}\", configured as \"{}\".", module_name, name); diff --git a/src/config.rs b/src/config.rs index 1196b73..e83c665 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,18 @@ pub fn read(path: &Path) -> Result { .map_err(Into::into) } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ActorConn { + pub machine: String, + pub actor: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InitiatorConn { + pub machine: String, + pub initiator: String, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { /// A list of address/port pairs to listen on. @@ -34,8 +46,8 @@ pub struct Config { pub mqtt_url: String, - pub actor_connections: Box<[(String, String)]>, - pub init_connections: Box<[(String, String)]>, + pub actor_connections: Box<[ActorConn]>, + pub init_connections: Box<[InitiatorConn]>, pub db_path: PathBuf, @@ -101,10 +113,12 @@ impl Default for Config { initiators, mqtt_url: "tcp://localhost:1883".to_string(), actor_connections: Box::new([ - ("Testmachine".to_string(), "Actor".to_string()), + ActorConn { machine: "Testmachine".to_string(), actor: "Actor".to_string() }, ]), init_connections: Box::new([ - ("Initiator".to_string(), "Testmachine".to_string()), + InitiatorConn { + initiator: "Initiator".to_string(), machine: "Testmachine".to_string() + }, ]), db_path: PathBuf::from("/run/bffh/database"), diff --git a/src/main.rs b/src/main.rs index 5543f88..07afdb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ use error::Error; use slog::Logger; use paho_mqtt::AsyncClient; -use crate::config::Config; +use crate::config::{ActorConn, Config, InitiatorConn}; fn main() { use clap::{crate_version, crate_description, crate_name}; @@ -182,18 +182,18 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { let mut network = network::Network::new(machines, actor_map, init_map); - for (a,b) in config.actor_connections.iter() { - if let Err(e) = network.connect_actor(a,b) { + for ActorConn { machine, actor } in config.actor_connections.iter() { + if let Err(e) = network.connect_actor(machine, actor) { error!(log, "{}", e); } - info!(log, "[Actor] Connected {} to {}", a, b); + info!(log, "[Actor] Connected {} to {}", machine, actor); } - for (a,b) in config.init_connections.iter() { - if let Err(e) = network.connect_init(a,b) { + for InitiatorConn { initiator, machine } in config.init_connections.iter() { + if let Err(e) = network.connect_init(initiator, machine) { error!(log, "{}", e); } - info!(log, "[Initi] Connected {} to {}", a, b); + info!(log, "[Initi] Connected {} to {}", initiator, machine); } for actor in actors.into_iter() { diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 3ae1975..843e2a3 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use slog::Logger; use crate::db::machine::Status; @@ -19,12 +20,23 @@ pub struct Shelly { log: Logger, name: String, client: mqtt::AsyncClient, + topic: String, } impl Shelly { - pub fn new(log: Logger, name: String, client: mqtt::AsyncClient) -> Self { - debug!(log, "Starting shelly module for {}", &name); - Shelly { log, name, client, } + pub fn new(log: Logger, name: String, client: mqtt::AsyncClient, params: &HashMap) -> Self { + let topic = if let Some(topic) = params.get("topic") { + format!("shellies/{}/relay/0/command", topic) + } else { + format!("shellies/{}/relay/0/command", name) + }; + debug!(log, + "Starting shelly module for {name} with topic '{topic}'", + name = &name, + topic = &topic, + ); + + Shelly { log, name, client, topic, } } /// Set the name to a new one. This changes the shelly that will be activated @@ -39,12 +51,11 @@ impl Shelly { impl Actuator for Shelly { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { info!(self.log, "Machine Status changed: {:?}", state); - let topic = format!("shellies/{}/relay/0/command", self.name); let pl = match state.state { Status::InUse(_) => "on", _ => "off", }; - let msg = mqtt::Message::new(topic, pl, 0); + let msg = mqtt::Message::new(&self.topic.clone(), pl, 0); let f = self.client.publish(msg).map(|_| ()); return Box::pin(f); From 6e91295cc0ab7a281b00db3d5ca301e861c3406d Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 5 Dec 2021 23:38:05 +0100 Subject: [PATCH 13/21] Fix URN --- examples/bffh.dhall | 8 +- src/api/machines.rs | 240 +++++++++++++++++--------------------------- src/db/access.rs | 39 ++++++- src/initiator.rs | 3 +- src/machine.rs | 46 +++++++-- 5 files changed, 174 insertions(+), 162 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index fd33768..e769991 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -22,10 +22,10 @@ { cmd = "./examples/fail-actor.sh" }} } - --, init_connections = [] : List { machine : Text, initiator : Text } -, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] -, initiators = --{=} - { Initiator = { module = "Dummy", params = {=} } } + , init_connections = [] : List { machine : Text, initiator : Text } +--, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] + , initiators = {=} + --{ Initiator = { module = "Dummy", params = {=} } } , listens = [ { address = "127.0.0.1", port = Some 59661 } , { address = "::1", port = Some 59661 } diff --git a/src/api/machines.rs b/src/api/machines.rs index 43ae558..6b1404a 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -8,13 +8,14 @@ use capnp::Error; use crate::db::machine::Status; use crate::api::machine::*; -use crate::schema::machine_capnp::machine::MachineState; +use crate::schema::machine_capnp::machine::{MachineState, Builder as MachineBuilder}; use crate::schema::machinesystem_capnp::machine_system; use crate::schema::machinesystem_capnp::machine_system::info as machines; use crate::network::Network; use crate::db::user::UserId; -use crate::db::access::{PermRule, admin_perm}; +use crate::db::access::{PermRule, admin_perm, Permission}; use crate::connection::Session; +use crate::machine::Machine as NwMachine; /// An implementation of the `Machines` API #[derive(Clone)] @@ -70,54 +71,15 @@ impl machines::Server for Machines { let permissions = &session.as_ref().unwrap().perms; let mut machines = results.get().init_machine_list(v.len() as u32); - for (i, (name, machine)) in v.into_iter().enumerate() { - let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - + for (i, (id, machine)) in v.into_iter().enumerate() { let mut builder = machines.reborrow().get(i as u32); - builder.set_id(&name); - builder.set_name(&machine.desc.name); - if let Some(ref desc) = machine.desc.description { - builder.set_description(desc); - } - if let Some(ref wiki) = machine.desc.wiki { - builder.set_wiki(wiki); - } - builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); - - let machineapi = Machine::new(user.clone(), perms, machine.clone()); - - let state = machine.get_status().await; - let s = match state { - Status::Free => MachineState::Free, - Status::Disabled => MachineState::Disabled, - Status::Blocked(_) => MachineState::Blocked, - Status::InUse(ref u) => { - if let Some(owner) = u.as_ref() { - if owner == user { - builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); - } - } - - MachineState::InUse - }, - Status::Reserved(_) => MachineState::Reserved, - Status::ToCheck(_) => MachineState::ToCheck, - }; - builder.set_state(s); - - if perms.write && state == Status::Free { - builder.set_use(capnp_rpc::new_client(machineapi.clone())); - } - if perms.manage { - //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); - //builder.set_check(capnp_rpc::new_client(machineapi.clone())); - builder.set_manage(capnp_rpc::new_client(machineapi.clone())); - } - if permissions.iter().any(|r| r.match_perm(&admin_perm())) { - builder.set_admin(capnp_rpc::new_client(machineapi.clone())); - } - - builder.set_info(capnp_rpc::new_client(machineapi)); + fill_machine_builder( + &mut builder, + &machine, + &permissions[..], + &id, + user, + ).await } Ok(()) @@ -148,50 +110,14 @@ impl machines::Server for Machines { if let Some(machine) = network.machines.get(&id) { let mut builder = results.get().init_machine(); - let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - builder.set_id(&id); - builder.set_name(&machine.desc.name); - if let Some(ref desc) = machine.desc.description { - builder.set_description(desc); - } - if let Some(ref wiki) = machine.desc.wiki { - builder.set_wiki(wiki); - } - builder.set_urn(&format!("urn:fabaccess:resource:{}", &id)); + fill_machine_builder( + &mut builder, + &machine, + &permissions[..], + &id, + user, + ).await - let machineapi = Machine::new(user.clone(), perms, machine.clone()); - let state = machine.get_status().await; - if perms.write && state == Status::Free { - builder.set_use(capnp_rpc::new_client(machineapi.clone())); - } - if perms.manage { - //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); - //builder.set_check(capnp_rpc::new_client(machineapi.clone())); - builder.set_manage(capnp_rpc::new_client(machineapi.clone())); - } - if permissions.iter().any(|r| r.match_perm(&admin_perm())) { - builder.set_admin(capnp_rpc::new_client(machineapi.clone())); - } - - - let s = match machine.get_status().await { - Status::Free => MachineState::Free, - Status::Disabled => MachineState::Disabled, - Status::Blocked(_) => MachineState::Blocked, - Status::InUse(u) => { - if let Some(owner) = u.as_ref() { - if owner == user { - builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); - } - } - MachineState::InUse - }, - Status::Reserved(_) => MachineState::Reserved, - Status::ToCheck(_) => MachineState::ToCheck, - }; - builder.set_state(s); - - builder.set_info(capnp_rpc::new_client(machineapi)); }; Ok(()) @@ -209,72 +135,34 @@ impl machines::Server for Machines { ) -> Promise<(), capnp::Error> { let rc = Rc::clone(&self.session); if self.session.borrow().is_some() { - let urn = { - let params = pry!(params.get()); - pry!(params.get_urn()) + let id: Option = { + let urn = pry!(pry!(params.get()).get_urn()); + let mut parts = urn.split_terminator(':'); + let part_urn = parts.next().map(|u| u == "urn").unwrap_or(false); + let part_fabaccess = parts.next().map(|f| f == "fabaccess").unwrap_or(false); + let part_resource = parts.next().map(|r| r == "resource").unwrap_or(false); + if !(part_urn && part_fabaccess && part_resource) { + return Promise::ok(()) + } + parts.next().map(|s| s.to_string()) }; - let mut parts = urn.split_terminator(':'); - let part_urn = parts.next().map(|u| u == "urn").unwrap_or(false); - let part_fabaccess = parts.next().map(|f| f == "fabaccess").unwrap_or(false); - let part_resource = parts.next().map(|r| r == "resource").unwrap_or(false); - if !(part_urn && part_fabaccess && part_resource) { - return Promise::ok(()) - } - - if let Some(name) = parts.next() { - let name = name.to_string(); + if let Some(id) = id { let network = self.network.clone(); let f = async move { let session = rc.borrow(); let user = &session.as_ref().unwrap().authzid; let permissions = &session.as_ref().unwrap().perms; - if let Some(machine) = network.machines.get(&name) { + if let Some(machine) = network.machines.get(&id) { let mut builder = results.get().init_machine(); - let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - builder.set_name(&name); - if let Some(ref desc) = machine.desc.description { - builder.set_description(desc); - } - if let Some(ref wiki) = machine.desc.wiki { - builder.set_wiki(wiki); - } - builder.set_urn(&format!("urn:fabaccess:resource:{}", &name)); - - let machineapi = Machine::new(user.clone(), perms, machine.clone()); - let state = machine.get_status().await; - if perms.write && state == Status::Free { - builder.set_use(capnp_rpc::new_client(machineapi.clone())); - } - if perms.manage { - //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); - //builder.set_check(capnp_rpc::new_client(machineapi.clone())); - builder.set_manage(capnp_rpc::new_client(machineapi.clone())); - } - if permissions.iter().any(|r| r.match_perm(&admin_perm())) { - builder.set_admin(capnp_rpc::new_client(machineapi.clone())); - } - - - let s = match machine.get_status().await { - Status::Free => MachineState::Free, - Status::Disabled => MachineState::Disabled, - Status::Blocked(_) => MachineState::Blocked, - Status::InUse(u) => { - if let Some(owner) = u.as_ref() { - if owner == user { - builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); - } - } - MachineState::InUse - }, - Status::Reserved(_) => MachineState::Reserved, - Status::ToCheck(_) => MachineState::ToCheck, - }; - builder.set_state(s); - - builder.set_info(capnp_rpc::new_client(machineapi)); + fill_machine_builder( + &mut builder, + &machine, + &permissions[..], + id, + user, + ).await }; Ok(()) @@ -288,3 +176,57 @@ impl machines::Server for Machines { } } } + +async fn fill_machine_builder( + builder: &mut MachineBuilder<'_>, + machine: &NwMachine, + permissions: &[PermRule], + id: impl AsRef, + user: &UserId, +) { + let name = &machine.desc.name; + let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); + builder.set_id(id.as_ref()); + builder.set_name(name); + if let Some(ref desc) = machine.desc.description { + builder.set_description(desc); + } + if let Some(ref wiki) = machine.desc.wiki { + builder.set_wiki(wiki); + } + builder.set_urn(&format!("urn:fabaccess:resource:{}", id.as_ref())); + + let machineapi = Machine::new(user.clone(), perms, machine.clone()); + let state = machine.get_status().await; + if perms.write && state == Status::Free { + builder.set_use(capnp_rpc::new_client(machineapi.clone())); + } + if perms.manage { + //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); + //builder.set_check(capnp_rpc::new_client(machineapi.clone())); + builder.set_manage(capnp_rpc::new_client(machineapi.clone())); + } + if permissions.iter().any(|r| r.match_perm(&admin_perm())) { + builder.set_admin(capnp_rpc::new_client(machineapi.clone())); + } + + + let s = match machine.get_status().await { + Status::Free => MachineState::Free, + Status::Disabled => MachineState::Disabled, + Status::Blocked(_) => MachineState::Blocked, + Status::InUse(u) => { + if let Some(owner) = u.as_ref() { + if owner == user { + builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); + } + } + MachineState::InUse + }, + Status::Reserved(_) => MachineState::Reserved, + Status::ToCheck(_) => MachineState::ToCheck, + }; + builder.set_state(s); + + builder.set_info(capnp_rpc::new_client(machineapi)); +} \ No newline at end of file diff --git a/src/db/access.rs b/src/db/access.rs index 7ab54c5..159dbfb 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -5,7 +5,7 @@ use slog::Logger; use std::sync::Arc; use std::fmt; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::cmp::Ordering; use std::path::Path; use std::fs; @@ -106,6 +106,43 @@ pub trait RoleDB { return Ok(output); } + + fn permitted_tally(&self, + roles: &mut HashSet, + role_id: &RoleIdentifier, + perm: &Permission + ) -> Result { + if let Some(role) = self.get_role(role_id)? { + // Only check and tally parents of a role at the role itself if it's the first time we + // see it + if !roles.contains(&role_id) { + for perm_rule in role.permissions.iter() { + if perm_rule.match_perm(perm) { + return Ok(true); + } + } + for parent in role.parents.iter() { + if self.permitted_tally(roles, parent, perm)? { + return Ok(true); + } + } + + roles.insert(role_id.clone()); + } + } + + Ok(false) + } + + fn is_permitted(&self, user: &UserData, perm: impl AsRef) -> Result { + let mut seen = HashSet::new(); + for role_id in user.roles.iter() { + if self.permitted_tally(&mut seen, role_id, perm.as_ref())? { + return Ok(true); + } + } + Ok(false) + } } impl RoleDB for HashMap { diff --git a/src/initiator.rs b/src/initiator.rs index f9d9bf6..dc8a190 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -117,8 +117,7 @@ impl Future for Initiator { debug!(this.log, "Sensor returned a new state"); this.future.take(); let f = this.machine.as_mut().map(|machine| { - unimplemented!() - //machine.request_state_change(user.as_ref(), state) + machine.request_state_change(user.as_ref(), state) }); this.state_change_fut = f; } diff --git a/src/machine.rs b/src/machine.rs index da2ed0f..4a6c02f 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -24,6 +24,7 @@ use slog::Logger; use crate::error::{Result, Error}; use crate::db::{access, Databases, MachineDB}; +use crate::db::access::AccessControl; use crate::db::machine::{MachineIdentifier, MachineState, Status}; use crate::db::user::{User, UserData, UserId}; @@ -62,15 +63,24 @@ pub struct Machine { pub id: MachineIdentifier, pub desc: MachineDescription, + access_control: Arc, + inner: Arc>, } impl Machine { - pub fn new(inner: Inner, id: MachineIdentifier, desc: MachineDescription) -> Self { + pub fn new( + inner: Inner, + id: MachineIdentifier, + desc: MachineDescription, + access_control: Arc + ) -> Self + { Self { id, inner: Arc::new(Mutex::new(inner)), desc, + access_control, } } @@ -79,9 +89,10 @@ impl Machine { desc: MachineDescription, state: MachineState, db: Arc, - ) -> Machine + access_control: Arc, + ) -> Machine { - Self::new(Inner::new(id.clone(), state, db), id, desc) + Self::new(Inner::new(id.clone(), state, db), id, desc, access_control) } pub fn do_state_change(&self, new_state: MachineState) @@ -98,6 +109,15 @@ impl Machine { Box::pin(f) } + pub fn request_state_change(&mut self, user: Option<&User>, new_state: MachineState) + -> BoxFuture<'static, Result> + { + let inner = self.inner.clone(); + Box::pin(async move { + Ok(ReturnToken::new(inner)) + }) + } + pub async fn get_status(&self) -> Status { let guard = self.inner.lock().await; guard.state.get_cloned().state @@ -140,7 +160,6 @@ pub struct Inner { reset: Option, previous: Option, - db: Arc, } @@ -277,6 +296,7 @@ pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) -> Result { let mut map = config.machines.clone(); + let access_control = db.access; let db = db.machine; let it = map.drain() @@ -284,10 +304,24 @@ pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) // TODO: Read state from the state db if let Some(state) = db.get(&k).unwrap() { debug!(log, "Loading old state from db for {}: {:?}", &k, &state); - (k.clone(), Machine::construct(k, v, state, db.clone())) + (k.clone(), + Machine::construct( + k, + v, + state, + db.clone(), + access_control.clone() + )) } else { debug!(log, "No old state found in db for {}, creating new.", &k); - (k.clone(), Machine::construct(k, v, MachineState::new(), db.clone())) + (k.clone(), + Machine::construct( + k, + v, + MachineState::new(), + db.clone(), + access_control.clone(), + )) } }); From 6b88191dc5e1e4b634564693404af91877ab11c2 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 5 Dec 2021 23:43:36 +0100 Subject: [PATCH 14/21] Set previous user on all state changes if required Fixes: #30 --- src/machine.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 4a6c02f..d2dad26 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -192,8 +192,20 @@ impl Inner { } pub fn do_state_change(&mut self, new_state: MachineState) { - let old_state = self.replace_state(new_state); - self.reset.replace(old_state); + let old_state = self.replace_state(new_state); + + // Set "previous user" if state change warrants it + match old_state.state { + Status::InUse(ref user) => { + self.previous = user.clone(); + }, + Status::ToCheck(ref user) => { + self.previous = Some(user.clone()); + }, + _ => {}, + } + + self.reset.replace(old_state); } pub fn read_state(&self) -> ReadOnlyMutable { From a09c3d3880ddada6700244e762f8f47a2ab689c3 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Tue, 7 Dec 2021 23:02:26 +0100 Subject: [PATCH 15/21] Fix #29 --- examples/bffh.dhall | 10 +-- src/api/machine.rs | 35 +-------- src/api/machines.rs | 2 +- src/config.rs | 2 +- src/db.rs | 3 +- src/db/access.rs | 97 ++++++++++++++++++++++++ src/initiator.rs | 55 ++++++++------ src/machine.rs | 181 +++++++++++++++++++++++++++++++------------- 8 files changed, 269 insertions(+), 116 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index e769991..84c5f70 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -8,7 +8,7 @@ , { machine = "Yetmore", actor = "FailBash"} ] , actors = - { Shelly_1234 = { module = "Shelly", params = + { Shelly1234 = { module = "Shelly", params = { topic = "Topic1234" }} , Bash = { module = "Process", params = { cmd = "./examples/actor.sh" @@ -22,10 +22,10 @@ { cmd = "./examples/fail-actor.sh" }} } - , init_connections = [] : List { machine : Text, initiator : Text } ---, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] - , initiators = {=} - --{ Initiator = { module = "Dummy", params = {=} } } + --, init_connections = [] : List { machine : Text, initiator : Text } + , init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] + , initiators = --{=} + { Initiator = { module = "Dummy", params = { uid = "Testuser" } } } , listens = [ { address = "127.0.0.1", port = Some 59661 } , { address = "::1", port = Some 59661 } diff --git a/src/api/machine.rs b/src/api/machine.rs index 65eb8d0..ab30a7f 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -6,46 +6,13 @@ use capnp::Error; use futures::FutureExt; -use crate::db::access::{PrivilegesBuf, PermRule}; +use crate::db::access::{PrivilegesBuf, PermRule, Perms}; use crate::db::user::UserId; use crate::db::machine::{Status, MachineState}; use crate::machine::Machine as NwMachine; use crate::schema::machine_capnp::machine::*; use crate::schema::machine_capnp::machine::MachineState as APIMState; -#[derive(Clone, Copy)] -pub struct Perms { - pub disclose: bool, - pub read: bool, - pub write: bool, - pub manage: bool, -} - -impl Perms { - pub fn get_for<'a, I: Iterator>(privs: &'a PrivilegesBuf, rules: I) -> Self { - let mut disclose = false; - let mut read = false; - let mut write = false; - let mut manage = false; - for rule in rules { - if rule.match_perm(&privs.disclose) { - disclose = true; - } - if rule.match_perm(&privs.read) { - read = true; - } - if rule.match_perm(&privs.write) { - write = true; - } - if rule.match_perm(&privs.manage) { - manage = true; - } - } - - Self { disclose, read, write, manage } - } -} - #[derive(Clone)] pub struct Machine { userid: UserId, diff --git a/src/api/machines.rs b/src/api/machines.rs index 6b1404a..878b57d 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -13,7 +13,7 @@ use crate::schema::machinesystem_capnp::machine_system; use crate::schema::machinesystem_capnp::machine_system::info as machines; use crate::network::Network; use crate::db::user::UserId; -use crate::db::access::{PermRule, admin_perm, Permission}; +use crate::db::access::{PermRule, admin_perm, Permission, Perms}; use crate::connection::Session; use crate::machine::Machine as NwMachine; diff --git a/src/config.rs b/src/config.rs index e83c665..b653c1b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,9 +5,9 @@ use std::collections::HashMap; use serde::{Serialize, Deserialize}; use crate::error::Result; -use crate::machine::MachineDescription; use crate::db::machine::MachineIdentifier; use crate::db::access::*; +use crate::machine::MachineDescription; pub fn read(path: &Path) -> Result { serde_dhall::from_file(path) diff --git a/src/db.rs b/src/db.rs index be802e6..5bec845 100644 --- a/src/db.rs +++ b/src/db.rs @@ -25,12 +25,13 @@ pub mod access; pub mod machine; pub type MachineDB = machine::internal::Internal; +pub type UserDB = user::Internal; #[derive(Clone)] pub struct Databases { pub access: Arc, pub machine: Arc, - pub userdb: Arc, + pub userdb: Arc, } const LMDB_MAX_DB: u32 = 16; diff --git a/src/db/access.rs b/src/db/access.rs index 159dbfb..813255d 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -11,6 +11,7 @@ use std::path::Path; use std::fs; use std::iter::FromIterator; use std::convert::{TryFrom, Into}; +use std::fmt::{Display, Formatter, Write}; use serde::{Serialize, Deserialize}; @@ -22,6 +23,81 @@ use crate::config::Config; use crate::db::user::UserData; pub use internal::Internal; +#[derive(Clone, Copy, Debug)] +pub struct Perms { + pub disclose: bool, + pub read: bool, + pub write: bool, + pub manage: bool, +} + +impl Display for Perms { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn part(f: &mut Formatter<'_>, fst: &mut bool, n: &'static str) -> fmt::Result { + if *fst { + *fst = false; + f.write_char(' ')?; + } else { + f.write_str("| ")?; + } + f.write_str(n)?; + f.write_char(' ') + } + + let mut fst = true; + f.write_char('(')?; + + if self.disclose { + part(f, &mut fst, "DISCLOSE")?; + } + if self.read { + part(f, &mut fst, "READ")?; + } + if self.write { + part(f, &mut fst, "WRITE")?; + } + if self.manage { + part(f, &mut fst, "MANAGE")?; + } + + f.write_char(')') + } +} + +impl Perms { + pub fn get_for<'a, I: Iterator>(privs: &'a PrivilegesBuf, rules: I) -> Self { + let mut disclose = false; + let mut read = false; + let mut write = false; + let mut manage = false; + for rule in rules { + if rule.match_perm(&privs.disclose) { + disclose = true; + } + if rule.match_perm(&privs.read) { + read = true; + } + if rule.match_perm(&privs.write) { + write = true; + } + if rule.match_perm(&privs.manage) { + manage = true; + } + } + + Self { disclose, read, write, manage } + } + + pub fn empty() -> Self { + Self { + disclose: false, + read: false, + write: false, + manage: false, + } + } +} + pub struct AccessControl { internal: HashMap, } @@ -566,6 +642,27 @@ impl TryFrom for PermRule { mod tests { use super::*; + #[test] + fn display_perm() { + let mut perm = Perms { + disclose: false, + read: false, + write: false, + manage: false + }; + println!("{}", perm); + assert_eq!("()", format!("{}", perm)); + + perm.disclose = true; + println!("{}", perm); + assert_eq!("( DISCLOSE )", format!("{}", perm)); + + perm.read = true; + perm.write = true; + println!("{}", perm); + assert_eq!("( DISCLOSE | READ | WRITE )", format!("{}", perm)); + } + #[test] fn permission_ord_test() { assert!(PermissionBuf::from_string("bffh.perm".to_string()) diff --git a/src/initiator.rs b/src/initiator.rs index dc8a190..39ca5d8 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -12,7 +12,7 @@ use paho_mqtt::AsyncClient; use futures::future::BoxFuture; use futures_signals::signal::{Signal, Mutable, MutableSignalCloned}; -use crate::machine::{Machine, ReturnToken}; +use crate::machine::Machine; use crate::db::machine::MachineState; use crate::db::user::{User, UserId, UserData}; @@ -22,7 +22,7 @@ use crate::error::Result; use crate::config::Config; pub trait Sensor { - fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; + fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; } type BoxSensor = Box; @@ -31,10 +31,9 @@ pub struct Initiator { log: Logger, signal: MutableSignalCloned>, machine: Option, - future: Option, MachineState)>>, + future: Option, MachineState)>>, // TODO: Prepare the init for async state change requests. - state_change_fut: Option>>, - token: Option, + state_change_fut: Option>>, sensor: BoxSensor, } @@ -46,7 +45,6 @@ impl Initiator { machine: None, future: None, state_change_fut: None, - token: None, sensor: sensor, } } @@ -92,13 +90,11 @@ impl Future for Initiator { debug!(this.log, "State change blocked"); return Poll::Pending; }, - Poll::Ready(Ok(tok)) => { + Poll::Ready(Ok(rt)) => { debug!(this.log, "State change returned ok"); // Explicity drop the future let _ = this.state_change_fut.take(); - // Store the given return token for future use - this.token.replace(tok); } Poll::Ready(Err(e)) => { info!(this.log, "State change returned err: {}", e); @@ -117,7 +113,7 @@ impl Future for Initiator { debug!(this.log, "Sensor returned a new state"); this.future.take(); let f = this.machine.as_mut().map(|machine| { - machine.request_state_change(user.as_ref(), state) + machine.request_state_change(user.as_ref(), state).unwrap() }); this.state_change_fut = f; } @@ -151,12 +147,12 @@ fn load_single( log: &Logger, name: &String, module_name: &String, - _params: &HashMap + params: &HashMap ) -> Option { match module_name.as_ref() { "Dummy" => { - Some(Box::new(Dummy::new(log))) + Some(Box::new(Dummy::new(log, params))) }, _ => { error!(log, "No initiator found with name \"{}\", configured as \"{}\"", @@ -169,34 +165,47 @@ fn load_single( pub struct Dummy { log: Logger, step: bool, + userid: Option, } impl Dummy { - pub fn new(log: &Logger) -> Self { - Self { log: log.new(o!("module" => "Dummy Initiator")), step: false } + pub fn new(log: &Logger, params: &HashMap) -> Self { + let userid = if let Some(uid) = params.get("uid") { + Some(UserId::new(uid.clone(), + params.get("subuid").map(String::from), + params.get("realm").map(String::from) + )) + } else { + None + }; + + let log = log.new(o!("module" => "Dummy Initiator")); + debug!(log, "Constructed dummy initiator with params: {:?}", params); + + Self { log, step: false, userid } + } } impl Sensor for Dummy { fn run_sensor(&mut self) - -> BoxFuture<'static, (Option, MachineState)> + -> BoxFuture<'static, (Option, MachineState)> { let step = self.step; self.step = !step; - info!(self.log, "Kicking off new dummy initiator state change: {}", step); + info!(self.log, "Kicking off new dummy initiator state change: {}, {:?}", + if step { "free" } else { "used" }, + &self.userid + ); + let userid = self.userid.clone(); let f = async move { Timer::after(std::time::Duration::from_secs(1)).await; if step { - return (None, MachineState::free()); + return (userid.clone(), MachineState::free()); } else { - let user = User::new( - UserId::new("test".to_string(), None, None), - UserData::new(vec![crate::db::access::RoleIdentifier::local_from_str("lmdb".to_string(), "testrole".to_string())], 0), - ); - let id = user.id.clone(); - return (Some(user), MachineState::used(Some(id))); + return (userid.clone(), MachineState::used(userid.clone())); } }; diff --git a/src/machine.rs b/src/machine.rs index d2dad26..255c669 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -23,10 +23,11 @@ use slog::Logger; use crate::error::{Result, Error}; -use crate::db::{access, Databases, MachineDB}; -use crate::db::access::AccessControl; +use crate::db::{access, Databases, MachineDB, UserDB}; +use crate::db::access::{AccessControl, Perms}; use crate::db::machine::{MachineIdentifier, MachineState, Status}; use crate::db::user::{User, UserData, UserId}; +use crate::Error::Denied; use crate::network::MachineMap; use crate::space; @@ -64,6 +65,8 @@ pub struct Machine { pub desc: MachineDescription, access_control: Arc, + userdb: Arc, + log: Logger, inner: Arc>, } @@ -73,7 +76,9 @@ impl Machine { inner: Inner, id: MachineIdentifier, desc: MachineDescription, - access_control: Arc + access_control: Arc, + userdb: Arc, + log: Logger, ) -> Self { Self { @@ -81,6 +86,8 @@ impl Machine { inner: Arc::new(Mutex::new(inner)), desc, access_control, + userdb, + log, } } @@ -90,9 +97,12 @@ impl Machine { state: MachineState, db: Arc, access_control: Arc, + userdb: Arc, + log: &Logger, ) -> Machine { - Self::new(Inner::new(id.clone(), state, db), id, desc, access_control) + let log = log.new(o!("machine" => id.clone())); + Self::new(Inner::new(id.clone(), state, db), id, desc, access_control, userdb, log) } pub fn do_state_change(&self, new_state: MachineState) @@ -109,13 +119,110 @@ impl Machine { Box::pin(f) } - pub fn request_state_change(&mut self, user: Option<&User>, new_state: MachineState) - -> BoxFuture<'static, Result> + pub fn request_state_change(&mut self, userid: Option<&UserId>, new_state: MachineState) + -> Result>> { let inner = self.inner.clone(); - Box::pin(async move { - Ok(ReturnToken::new(inner)) - }) + let perms = if let Some(userid) = userid { + if let Some(user) = self.userdb.get_user(&userid.uid)? { + let roles = self.access_control.collect_permrules(&user.data)?; + Perms::get_for(&self.desc.privs, roles.iter()) + } else { + Perms::empty() + } + } else { + Perms::empty() + }; + + debug!(self.log, "State change requested: {:?}, {:?}, {}", + &userid, + new_state, + perms, + ); + + if perms.manage { + Ok(Box::pin(async move { + let mut guard = inner.lock().await; + guard.do_state_change(new_state); + Ok(()) + })) + } else if perms.write { + let userid = userid.map(|u| u.clone()); + let f = async move { + let mut guard = inner.lock().await; + // Match (current state, new state) for permission + if + match (guard.read_state().lock_ref().state.clone(), &new_state.state) { + // Going from available to used by the person requesting is okay. + (Status::Free, Status::InUse(who)) + // Check that the person requesting does not request for somebody else. + // *That* is manage privilege. + if who.as_ref() == userid.as_ref() => true, + + // Reserving things for ourself is okay. + (Status::Free, Status::Reserved(whom)) + if userid.as_ref() == Some(whom) => true, + + // Returning things we've been using is okay. This includes both if + // they're being freed or marked as to be checked. + (Status::InUse(who), Status::Free | Status::ToCheck(_)) + if who.as_ref() == userid.as_ref() => true, + + // Un-reserving things we reserved is okay + (Status::Reserved(whom), Status::Free) + if userid.as_ref() == Some(&whom) => true, + // Using things that we've reserved is okay. But the person requesting + // that has to be the person that reserved the machine. Otherwise + // somebody could make a machine reserved by a different user as used by + // that different user but use it themself. + (Status::Reserved(whom), Status::InUse(who)) + if userid.as_ref() == Some(&whom) + && who.as_ref() == Some(&whom) + => true, + + // Default is deny. + _ => false + } + { + // Permission granted + guard.do_state_change(new_state); + Ok(()) + } else { + Err(Denied) + } + }; + + Ok(Box::pin(f)) + } else { + let userid = userid.map(|u| u.clone()); + let f = async move { + let mut guard = inner.lock().await; + // Match (current state, new state) for permission + if + match (guard.read_state().lock_ref().state.clone(), &new_state.state) { + // Returning things we've been using is okay. This includes both if + // they're being freed or marked as to be checked. + (Status::InUse(who), Status::Free | Status::ToCheck(_)) + if who.as_ref() == userid.as_ref() => true, + + // Un-reserving things we reserved is okay + (Status::Reserved(whom), Status::Free) + if userid.as_ref() == Some(&whom) => true, + + // Default is deny. + _ => false + } + { + // Permission granted + guard.do_state_change(new_state); + Ok(()) + } else { + Err(Denied) + } + }; + + Ok(Box::pin(f)) + } } pub async fn get_status(&self) -> Status { @@ -244,39 +351,6 @@ impl Inner { } } -//pub type ReturnToken = futures::channel::oneshot::Sender<()>; -pub struct ReturnToken { - f: Option>, -} - -impl ReturnToken { - pub fn new(inner: Arc>) -> Self { - let f = async move { - let mut guard = inner.lock().await; - guard.reset_state(); - }; - - Self { f: Some(Box::pin(f)) } - } -} - -impl Future for ReturnToken { - type Output = (); // FIXME: This should probably be a Result<(), Error> - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let mut this = &mut *self; - - match this.f.as_mut().map(|f| Future::poll(Pin::new(f), cx)) { - None => Poll::Ready(()), // TODO: Is it saner to return Pending here? This can only happen after the future completed - Some(Poll::Pending) => Poll::Pending, - Some(Poll::Ready(())) => { - let _ = this.f.take(); // Remove the future to not poll after completion - Poll::Ready(()) - } - } - } -} - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] /// A description of a machine /// @@ -305,24 +379,27 @@ impl MachineDescription { } pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) - -> Result + -> Result { let mut map = config.machines.clone(); let access_control = db.access; - let db = db.machine; + let machinedb = db.machine; + let userdb = db.userdb; let it = map.drain() .map(|(k,v)| { // TODO: Read state from the state db - if let Some(state) = db.get(&k).unwrap() { + if let Some(state) = machinedb.get(&k).unwrap() { debug!(log, "Loading old state from db for {}: {:?}", &k, &state); (k.clone(), Machine::construct( - k, - v, - state, - db.clone(), - access_control.clone() + k, + v, + state, + machinedb.clone(), + access_control.clone(), + userdb.clone(), + log, )) } else { debug!(log, "No old state found in db for {}, creating new.", &k); @@ -331,8 +408,10 @@ pub fn load(config: &crate::config::Config, db: Databases, log: &Logger) k, v, MachineState::new(), - db.clone(), + machinedb.clone(), access_control.clone(), + userdb.clone(), + log, )) } }); From eb2e24a48ccf2a11bccd2ff2c7391ad2268ddba1 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 9 Dec 2021 18:17:35 +0100 Subject: [PATCH 16/21] Add rustls dependencies --- Cargo.lock | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 ++ 2 files changed, 167 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index ec8c80a..03ae416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,6 +154,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "async-rustls" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378" +dependencies = [ + "futures-lite", + "rustls", + "webpki", +] + [[package]] name = "async-task" version = "4.0.3" @@ -294,6 +305,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + [[package]] name = "byte-tools" version = "0.3.1" @@ -518,6 +535,7 @@ name = "diflouroborane" version = "0.3.1" dependencies = [ "async-channel", + "async-rustls", "async-trait", "bincode", "capnp", @@ -539,6 +557,8 @@ dependencies = [ "rand", "rsasl", "rust-argon2", + "rustls", + "rustls-pemfile", "serde", "serde_dhall", "signal-hook", @@ -1057,6 +1077,15 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1556,6 +1585,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rsasl" version = "1.4.0" @@ -1585,6 +1629,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + [[package]] name = "rustversion" version = "1.0.5" @@ -1606,6 +1672,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.130" @@ -1764,6 +1840,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1961,6 +2043,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.2.2" @@ -2018,6 +2106,80 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index c588f69..ba9f301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,11 @@ async-channel = "1.6.1" easy-parallel = "3.1.0" genawaiter = "0.99.1" +# TLS +rustls = "0.19" +rustls-pemfile = "0.2" +async-rustls = "0.2" + [build-dependencies] capnpc = "0.14.4" # Used in build.rs to iterate over all files in schema/ From 83f5fe8265cbf6a715e1645d157b7538b211a4b1 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 9 Dec 2021 20:54:54 +0100 Subject: [PATCH 17/21] Implement TLS handling --- examples/bffh.dhall | 10 +++-- src/config.rs | 7 ++++ src/connection.rs | 97 +++++++++++++++++++++++++++++++++++++++++---- src/db/user.rs | 2 +- src/error.rs | 10 +++++ src/server.rs | 46 ++++++++++++++++++++- 6 files changed, 159 insertions(+), 13 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index 84c5f70..4a57c51 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -22,10 +22,10 @@ { cmd = "./examples/fail-actor.sh" }} } - --, init_connections = [] : List { machine : Text, initiator : Text } - , init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] - , initiators = --{=} - { Initiator = { module = "Dummy", params = { uid = "Testuser" } } } + , init_connections = [] : List { machine : Text, initiator : Text } + --, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] + , initiators = {=} + --{ Initiator = { module = "Dummy", params = { uid = "Testuser" } } } , listens = [ { address = "127.0.0.1", port = Some 59661 } , { address = "::1", port = Some 59661 } @@ -74,4 +74,6 @@ ] } } +, certfile = "examples/self-signed-cert.pem" +, keyfile = "examples/self-signed-key.pem" } diff --git a/src/config.rs b/src/config.rs index b653c1b..79f1a0c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -52,6 +52,10 @@ pub struct Config { pub db_path: PathBuf, pub roles: HashMap, + + /// Path to a certificate chain to be used + pub certfile: PathBuf, + pub keyfile: PathBuf, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -123,6 +127,9 @@ impl Default for Config { db_path: PathBuf::from("/run/bffh/database"), roles: HashMap::new(), + + certfile: PathBuf::from("/etc/bffh/pub.crt"), + keyfile: PathBuf::from("/etc/bffh/priv.key"), } } } diff --git a/src/connection.rs b/src/connection.rs index e2cbcd5..967ced3 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,20 +1,27 @@ -use futures::FutureExt; +use std::fmt::Debug; +use std::ops::DerefMut; +use futures::{AsyncRead, AsyncWrite, FutureExt}; use std::future::Future; +use std::io::{IoSlice, IoSliceMut}; +use std::pin::Pin; +use std::rc::Rc; use std::sync::Arc; +use std::task::{Context, Poll}; +use async_rustls::server::TlsStream; use slog::Logger; use smol::lock::Mutex; -use smol::net::TcpStream; use crate::api::Bootstrap; use crate::error::Result; use capnp_rpc::{rpc_twoparty_capnp, twoparty}; +use futures_util::{pin_mut, ready}; use crate::schema::connection_capnp; -use crate::db::access::{AccessControl, PermRule, RoleIdentifier}; +use crate::db::access::{PermRule, RoleIdentifier}; use crate::db::user::UserId; use crate::db::Databases; use crate::network::Network; @@ -75,14 +82,17 @@ impl ConnectionHandler { Self { log, db, network } } - pub fn handle(&mut self, stream: TcpStream) -> impl Future> { - info!(self.log, "New connection from on {:?}", stream); + pub fn handle(&mut self, stream: TlsStream) + -> impl Future> + { + let conn = Connection::new(stream); + let boots = Bootstrap::new(self.log.new(o!()), self.db.clone(), self.network.clone()); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); let network = twoparty::VatNetwork::new( - stream.clone(), - stream, + conn.clone(), + conn, rpc_twoparty_capnp::Side::Server, Default::default(), ); @@ -92,3 +102,76 @@ impl ConnectionHandler { rpc_system.map(|r| r.map_err(Into::into)) } } + +struct Connection { + inner: Rc>>, +} + +impl Connection { + pub fn new(stream: TlsStream) -> Self { + Self { + inner: Rc::new(Mutex::new(stream)), + } + } +} + +impl Clone for Connection { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone() + } + } +} + + +impl AsyncRead for Connection { + fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_read(cx, buf) + } + + fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_read_vectored(cx, bufs) + } +} + +impl AsyncWrite for Connection { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_write(cx, buf) + } + + fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_write_vectored(cx, bufs) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let f = self.inner.lock(); + pin_mut!(f); + let mut guard = ready!(f.poll(cx)); + let stream = guard.deref_mut(); + Pin::new(stream).poll_close(cx) + } +} \ No newline at end of file diff --git a/src/db/user.rs b/src/db/user.rs index b212647..3a200dd 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -134,7 +134,7 @@ pub fn load_file>(path: P) -> Result> { } pub fn init(log: Logger, _config: &Config, env: Arc) -> Result { - let mut flags = lmdb::DatabaseFlags::empty(); + let flags = lmdb::DatabaseFlags::empty(); let db = env.create_db(Some("userdb"), flags)?; debug!(&log, "Opened user db successfully."); diff --git a/src/error.rs b/src/error.rs index 801b435..e61cb00 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,6 +29,7 @@ pub enum Error { BadVersion((u32,u32)), Argon2(argon2::Error), EventNetwork(network::Error), + RustTls(rustls::TLSError), Denied, } @@ -83,6 +84,9 @@ impl fmt::Display for Error { Error::EventNetwork(e) => { e.fmt(f) } + Error::RustTls(e) => { + write!(f, "TLS Error: {}", e) + } } } } @@ -171,4 +175,10 @@ impl From for Error { } } +impl From for Error { + fn from(e: rustls::TLSError) -> Error { + Error::RustTls(e) + } +} + pub(crate) type Result = std::result::Result; diff --git a/src/server.rs b/src/server.rs index c51e8fb..4b9c500 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use std::fs::File; use slog::Logger; use crate::config; @@ -13,10 +14,15 @@ use smol::Executor; use futures::prelude::*; use std::io; +use std::io::BufReader; use std::sync::Arc; use std::os::unix::io::AsRawFd; +use std::path::Path; +use async_rustls::TlsAcceptor; +use rustls::{Certificate, KeyLogFile, NoClientAuth, PrivateKey, ServerConfig}; +use rustls_pemfile::Item; use signal_hook::low_level::pipe as sigpipe; use crate::db::Databases; @@ -46,6 +52,33 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw io::Result::Ok(LoopResult::Stop) }); + info!(log, "Reading certificate chain file"); + let mut certfp = BufReader::new(File::open(&config.certfile)?); + let certs = rustls_pemfile::certs(&mut certfp)? + .into_iter() + .map(Certificate) + .collect(); + info!(log, "Reading private key file"); + let mut keyfp = BufReader::new(File::open(&config.keyfile)?); + let mut tls_config = ServerConfig::new(Arc::new(NoClientAuth)); + tls_config.key_log = Arc::new(KeyLogFile::new()); + if let Some(path) = std::env::var_os("SSLKEYLOGFILE") { + let path = Path::new(&path); + warn!(log, "TLS SECRET LOGGING ENABLED! This will write all connection secrets to file {}!", + path.display()); + } + match rustls_pemfile::read_one(&mut keyfp)? { + Some(rustls_pemfile::Item::PKCS8Key(key) | rustls_pemfile::Item::RSAKey(key)) => { + let key = PrivateKey(key); + tls_config.set_single_cert(certs, key)?; + } + _ => { + error!(log, "private key file must contain a PEM-encoded private key"); + return Ok(()); + } + } + let tls_acceptor: TlsAcceptor = Arc::new(tls_config).into(); + // Bind to each address in config.listens. // This is a Stream over Futures so it will do absolutely nothing unless polled to completion let listeners_s: futures::stream::Collect<_, Vec> @@ -97,18 +130,29 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw inner_log.new(o!()) }; + let db = db.clone(); let network = network.clone(); let tlog = inner_log.new(o!()); + + let tls_acceptor_clone = tls_acceptor.clone(); std::thread::spawn(move || { + let tls_acceptor = tls_acceptor_clone; let local_ex = LocalExecutor::new(); + info!(tlog, "New connection from on {:?}", socket); let mut handler = connection::ConnectionHandler::new(tlog, db, network); // We handle the error using map_err - let f = handler.handle(socket) + let log2 = log.clone(); + let f = tls_acceptor.accept(socket) .map_err(move |e| { error!(log, "Error occured during protocol handling: {}", e); }) + .and_then(|stream| { + handler.handle(stream).map_err(move |e| { + error!(log2, "Error occured during protocol handling: {}", e); + }) + }) // Void any and all results since pool.spawn allows no return value. .map(|_| ()); From 9571afbcc78b60c813953e750a55cbf2d85fcb7a Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 9 Dec 2021 20:56:18 +0100 Subject: [PATCH 18/21] Add development cert/key files --- examples/self-signed-cert.pem | 19 +++++++++++++++++++ examples/self-signed-key.pem | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 examples/self-signed-cert.pem create mode 100644 examples/self-signed-key.pem diff --git a/examples/self-signed-cert.pem b/examples/self-signed-cert.pem new file mode 100644 index 0000000..89f056d --- /dev/null +++ b/examples/self-signed-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFzCCAf+gAwIBAgIUDr+1F3zzyza+soLtiurKEXW9pGIwDQYJKoZIhvcNAQEL +BQAwGzEZMBcGA1UEAwwQYmZmaC1kZXZlbG9wbWVudDAeFw0yMTEyMDkxODQ2Mjla +Fw0zMTEyMDkxODQ2MjlaMBsxGTAXBgNVBAMMEGJmZmgtZGV2ZWxvcG1lbnQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGwNy7yGaURR08dWfoDmnJeyx1 +0FVRmozoGCIb3Oj6c2t+84QUxqTdknE7Cdcz5Wi1o0x2CWPZG4z1vgTaCcJVhcME +hxn+7eK1NtDQEjs8Ojs7uaraVvooIe8R7jat0qs7Dmf8RO9P0I4MMZlvijhI7aLw +0C6vNsr1ebeppIiwO5aUuCGuKqxJGghHeqZv18ZcPayunyNrxMC6uyX7y6nUVkfq +x0m9gDsN112Iv9Dd/ZE5Gxivm8jZvVUGZgJD2szK7zbeCDeo5aU3cRWfYaoN0QDx +AKmo4bjciJzfMDDgvcIBq9lGS3FxEv394Mc5YX/ZdP+KRTiHcYCXfBzr3B6HAgMB +AAGjUzBRMB0GA1UdDgQWBBTtUvvWXlo5tU8cEoxbs5UJdodOVDAfBgNVHSMEGDAW +gBTtUvvWXlo5tU8cEoxbs5UJdodOVDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAB3IxRnWi/LrxCvlHNaWEi3ZvlbN+KpegWZeKtjHwQoizhR/Fi +SMC7z4y6trJE7LXUOSb9Pu8QQSvpVQZd3W4XCPZMl10Lt7iV8vc3pVviCseDnT9r +X1gXdbeuyYm9lE8KtlhX03jD/CiEx7Qe/q8Rc20AQIKAAJzvl7sXU2tmJ5kpzMEO +v5czlLaX2ajlD/QMgNBUuDyw6wPo3wx9Khph484RygN2LHeT6kfu/PBiF0dGDTUu +mgxg4K0GfwTcHgtz5Bag/HyuuJEKx8Wv7jth59PyKPT+lMVBznxIm3gLS5U+Nnr1 +uAws8dgLXRlPo5HJORuJCcZWVBClruZUpyDT +-----END CERTIFICATE----- diff --git a/examples/self-signed-key.pem b/examples/self-signed-key.pem new file mode 100644 index 0000000..08139db --- /dev/null +++ b/examples/self-signed-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGwNy7yGaURR08 +dWfoDmnJeyx10FVRmozoGCIb3Oj6c2t+84QUxqTdknE7Cdcz5Wi1o0x2CWPZG4z1 +vgTaCcJVhcMEhxn+7eK1NtDQEjs8Ojs7uaraVvooIe8R7jat0qs7Dmf8RO9P0I4M +MZlvijhI7aLw0C6vNsr1ebeppIiwO5aUuCGuKqxJGghHeqZv18ZcPayunyNrxMC6 +uyX7y6nUVkfqx0m9gDsN112Iv9Dd/ZE5Gxivm8jZvVUGZgJD2szK7zbeCDeo5aU3 +cRWfYaoN0QDxAKmo4bjciJzfMDDgvcIBq9lGS3FxEv394Mc5YX/ZdP+KRTiHcYCX +fBzr3B6HAgMBAAECggEAS5DGG6ssvRCt9e+ZatQYCl+HXt+voJAHJLMQPNG3zokV +hLXnMNL5mbh0zoKGTJfbQLvudS5KxR/BbykoxRFSzptFszH+gztEp6tIpuNXnCVz +odiMiejpwVptf763EU14hsKKbJJ0/j6H00EEWjEOB0Q6YB52sW0+qyf02U3SHlZN +k2PZYkpHi3YCONtOGj7jOdW7M3RfvcBNg9EW7fZc1KkiRAlscUAYLMkKcOLevil0 +lUuF/JWj4iH22Oq6+JeSiecf6izF6lyIGvMXPry+woa8Iq0BBdmbZsK7r/Pa+wlz ++E6xHGn2rcyrnYB2pPc+RfHhYlodaOo69DxAYlRYaQKBgQDxnKySmlcfHe8lMqR4 +2LvqMyGjNRo2+q9VZYej2mvr6+TGyd7Op/fRJ1t5f9DgtINomNQYSOtYAsPiEnl+ +43z2N/Rdh6XSmOj4gLkDeiYNSpy86L/F39uCWZpkkqiy2zxYLWOs15MA0GWOtQGh +dz4cM0b/jyZOdHZR//L5WiMLawKBgQDSltEfQKqCEKJTqp4SU9BCHQmyheIjepY2 +eKakgcsjpFjRBK2VrUalDLOQV74rtd7wp8F8kqqPJpRshb+oVuDCg6JB17UxYd34 +iB+5cMdLRpg8f0HOqcYz4KOql2QhJQhFc3jzY7n1piPEhFO/MqFwmlUB4RdgJ3ix +HqX+F/T8VQKBgGos76l9KcwC25T9LEnu9KV20tFmBJ8kiuh8NZ9L3SFQCLlS/RbT +uZOwOAKsqJ4WtajBgHMrmECU9n/inoGkdsW80SZI9hYWHEsYRjXA9/ffUgGyRpQu +S8h8l9yalogC0AHv8F2EXpV8/yQ3ZwAN5r19yzWDMtJHW7etQplRgxUBAoGAGeOy +t+3iSHU1D6YlIsmtC8O4Int1LrluaCnzCrxuNeaJiMDTelhAHCBwnuk6lvMYAmwN +THxXfZvXmXPj+RUdMqyuMPwM6ZJHkLtjcw/bYHTAWIeolnimxk/yrxFHnQ+Jcchd +cUasYPfY49sE1Lerw0Ul+EIs9oRDwTqsW42kb7UCgYEA2y+oc7Fz2eq38hSbTfy7 +OcATtny+xQ1+4IELtQIP7VctkMInJs57J+vS//IT41P0L2K1YjvL8RacnvG7yMvP +GnwHBcKgvL6zuoy11I3zPPYtbKGwcJoVGomPX7W0csfl4gdST3uugd9iCDEB8NsS +QmOYM/dk8x8aWpndBjRF5ig= +-----END PRIVATE KEY----- From f397e1e63699ec85305fd0de1d27446682815fad Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 9 Dec 2021 21:54:57 +0100 Subject: [PATCH 19/21] Better tls connection handling with smol::io::split --- src/connection.rs | 80 +++-------------------------------------------- src/server.rs | 23 ++++++-------- 2 files changed, 14 insertions(+), 89 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 967ced3..6072463 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -18,6 +18,7 @@ use crate::error::Result; use capnp_rpc::{rpc_twoparty_capnp, twoparty}; use futures_util::{pin_mut, ready}; +use smol::io::split; use crate::schema::connection_capnp; @@ -85,14 +86,14 @@ impl ConnectionHandler { pub fn handle(&mut self, stream: TlsStream) -> impl Future> { - let conn = Connection::new(stream); + let (mut reader, mut writer) = split(stream); let boots = Bootstrap::new(self.log.new(o!()), self.db.clone(), self.network.clone()); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); let network = twoparty::VatNetwork::new( - conn.clone(), - conn, + reader, + writer, rpc_twoparty_capnp::Side::Server, Default::default(), ); @@ -101,77 +102,4 @@ impl ConnectionHandler { // Convert the error type to one of our errors rpc_system.map(|r| r.map_err(Into::into)) } -} - -struct Connection { - inner: Rc>>, -} - -impl Connection { - pub fn new(stream: TlsStream) -> Self { - Self { - inner: Rc::new(Mutex::new(stream)), - } - } -} - -impl Clone for Connection { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone() - } - } -} - - -impl AsyncRead for Connection { - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_read(cx, buf) - } - - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_read_vectored(cx, bufs) - } -} - -impl AsyncWrite for Connection { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_write(cx, buf) - } - - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_write_vectored(cx, bufs) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let f = self.inner.lock(); - pin_mut!(f); - let mut guard = ready!(f.poll(cx)); - let stream = guard.deref_mut(); - Pin::new(stream).poll_close(cx) - } } \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index 4b9c500..7775330 100644 --- a/src/server.rs +++ b/src/server.rs @@ -138,28 +138,25 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw let tls_acceptor_clone = tls_acceptor.clone(); std::thread::spawn(move || { let tls_acceptor = tls_acceptor_clone; - let local_ex = LocalExecutor::new(); info!(tlog, "New connection from on {:?}", socket); + let peer = socket.peer_addr(); let mut handler = connection::ConnectionHandler::new(tlog, db, network); // We handle the error using map_err - let log2 = log.clone(); let f = tls_acceptor.accept(socket) - .map_err(move |e| { - error!(log, "Error occured during protocol handling: {}", e); - }) - .and_then(|stream| { - handler.handle(stream).map_err(move |e| { - error!(log2, "Error occured during protocol handling: {}", e); - }) - }) - // Void any and all results since pool.spawn allows no return value. - .map(|_| ()); + .map_err(Error::IO) + .and_then(|stream| handler.handle(stream)); // Spawn the connection context onto the local executor since it isn't Send // Also `detach` it so the task isn't canceled as soon as it's dropped. // TODO: Store all those tasks to have a easier way of managing them? - smol::block_on(f); + if let Err(e) = smol::block_on(f) { + error!(log, "Error occurred during connection handling: {:?}", e) + } else if let Ok(peer) = peer { + debug!(log, "Closed connection with {:?}", peer); + } else { + debug!(log, "Closed connection with unknown peer"); + } }); }, Err(e) => { From ac6dbefa6f125121e4c3220a6d2ce561e52bd462 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 11 Jan 2022 17:50:20 +0000 Subject: [PATCH 20/21] Update INSTALL.md --- INSTALL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 0be015f..84d5799 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,5 +1,7 @@ ## Installation +Full documentation can be found at : https://fab-access.readthedocs.io/ + Currently there are no distribution packages available. However installation is reasonably straight-forward, since Diflouroborane compiles into a single mostly static binary with few dependencies. From 1020e21e24a27f280c36935518176a61f8d6071d Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 11 Jan 2022 17:52:41 +0000 Subject: [PATCH 21/21] Update INSTALL.md --- INSTALL.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 84d5799..2bd2a87 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,6 +1,8 @@ ## Installation -Full documentation can be found at : https://fab-access.readthedocs.io/ +A more complete documentation can be found at : https://fab-access.readthedocs.io/ + +----- Currently there are no distribution packages available. However installation is reasonably straight-forward, since Diflouroborane compiles into a single