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(), + )) } });