diff --git a/src/db/access.rs b/src/db/access.rs index 75dd117..81e7dc6 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -60,6 +60,10 @@ impl AccessControl { return Ok(false); } + + pub fn dump_roles(&self) -> Result> { + self.internal.dump_roles() + } } impl fmt::Debug for AccessControl { @@ -90,13 +94,13 @@ pub trait RoleDB { /// implementations. fn check_roles(&self, roles: &[RoleIdentifier], perm: &Permission) -> Result { // Tally all roles. Makes dependent roles easier - let mut roleset = HashSet::new(); + let mut roleset = HashMap::new(); for roleID in roles { self.tally_role(&mut roleset, roleID)?; } // Iter all unique role->permissions we've found and early return on match. - for role in roleset.iter() { + for (_roleid, role) in roleset.iter() { for perm_rule in role.permissions.iter() { if perm_rule.match_perm(&perm) { return Ok(true); @@ -111,16 +115,16 @@ pub trait RoleDB { /// /// A Default implementation exists which adapter may overwrite with more efficient /// implementations. - fn tally_role(&self, roles: &mut HashSet, roleID: &RoleIdentifier) -> Result<()> { + fn tally_role(&self, roles: &mut HashMap, roleID: &RoleIdentifier) -> Result<()> { if let Some(role) = self.get_role(roleID)? { // 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) { + if !roles.contains_key(&roleID) { for parent in role.parents.iter() { self.tally_role(roles, parent)?; } - roles.insert(role); + roles.insert(roleID.clone(), role); } } @@ -371,7 +375,7 @@ impl fmt::Display for PermissionBuf { } #[repr(transparent)] -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, Debug)] /// A borrowed permission string /// /// Permissions have total equality and partial ordering. diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index b4bd94e..7fb22a1 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::HashMap; use std::convert::TryInto; @@ -11,7 +11,7 @@ use flexbuffers; use serde::{Serialize, Deserialize}; use slog::Logger; -use lmdb::{Environment, Transaction, RwTransaction, Cursor}; +use lmdb::{Environment, Transaction, RwTransaction, Cursor, Iter}; use crate::config::Settings; use crate::error::Result; @@ -36,36 +36,45 @@ impl Internal { pub fn _check>(&self, txn: &T, user: &UserData, perm: &P) -> Result { + debug!(self.log, "Checking user {:?} for permission {:?}", user, perm.as_ref()); // Tally all roles. Makes dependent roles easier - let mut roles = HashSet::new(); + let mut roles = HashMap::new(); for roleID in user.roles.iter() { + debug!(self.log, "Tallying role {} for its parents", roleID); self._tally_role(txn, &mut roles, roleID)?; } // Iter all unique role->permissions we've found and early return on match. // TODO: Change this for negative permissions? - for role in roles.iter() { + for (roleid, role) in roles.iter() { + debug!(self.log, " checking role {}", roleid); for perm_rule in role.permissions.iter() { if perm_rule.match_perm(perm) { + debug!(self.log, " matches permission rule {}", perm_rule); return Ok(true); } + trace!(self.log, " rejecting permission rule {}", perm_rule); } } + debug!(self.log, "Checked all roles, rejecting access"); + return Ok(false); } - fn _tally_role(&self, txn: &T, roles: &mut HashSet, roleID: &RoleIdentifier) -> Result<()> { + fn _tally_role(&self, txn: &T, roles: &mut HashMap, roleID: &RoleIdentifier) -> Result<()> { if let Some(role) = self._get_role(txn, roleID)? { // 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) { + if !roles.contains_key(&roleID) { for parent in role.parents.iter() { self._tally_role(txn, roles, parent)?; } - roles.insert(role); + roles.insert(roleID.clone(), role); } + } else { + info!(self.log, "Did not find role {} while trying to tally", roleID); } Ok(()) @@ -82,55 +91,57 @@ impl Internal { } } - fn put_role(&self, txn: &mut RwTransaction, roleID: &RoleIdentifier, role: Role) -> Result<()> { - let bytes = flexbuffers::to_vec(role)?; - let string = format!("{}", roleID); - txn.put(self.roledb, &string.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; + fn put_role(&self, txn: &mut RwTransaction, roleID: &RoleIdentifier, role: Role) -> Result<()> { + let bytes = flexbuffers::to_vec(role)?; + let string = format!("{}", roleID); + txn.put(self.roledb, &string.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; - Ok(()) - } + Ok(()) + } - pub fn dump_db(&mut self, txn: &T, mut path: PathBuf) -> Result<()> { - path.push("roles"); - let mut k = Ok(()); - if !path.is_dir() { - k = fs::create_dir(&path); - } - if let Err(e) = k { - error!(self.log, "Failed to create 'roles' directory: {}, skipping!", e); - return Ok(()) - } else { - // Rust's stdlib considers the last element the file name even when it's a directory so - // we have to put a dummy here for .set_filename() to work correctly - path.push("dummy"); - self.dump_roles(txn, path.clone())?; - path.pop(); - } - path.pop(); - Ok(()) - } + pub fn dump_roles(&self) -> Result> { + let txn = self.env.begin_ro_txn()?; + self.dump_roles_txn(&txn) + } + pub fn dump_roles_txn(&self, txn: &T) -> Result> { + let mut cursor = txn.open_ro_cursor(self.roledb)?; - fn dump_roles(&mut self, txn: &T, mut path: PathBuf) -> Result<()> { - // TODO implement this for the new format - unimplemented!() - } + let mut vec = Vec::new(); + for r in cursor.iter_start() { + match r { + Ok( (k,v) ) => { + let role_id_str = unsafe { std::str::from_utf8_unchecked(k) }; + let role_id = role_id_str.parse::().unwrap(); + let role = flexbuffers::from_slice(v)?; + vec.push((role_id, role)); + }, + Err(e) => return Err(e.into()), + } + } - pub fn load_roles>(&self, path: P) -> Result<()> { - let mut txn = self.env.begin_rw_txn()?; - self.load_roles_txn(&mut txn, path.as_ref()) - } - fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> { - let roles = Role::load_file(path)?; + Ok(vec) + } - for (k,v) in roles.iter() { - self.put_role(txn, k, v.clone())?; - } + pub fn load_roles>(&self, path: P) -> Result<()> { + let mut txn = self.env.begin_rw_txn()?; + self.load_roles_txn(&mut txn, path.as_ref())?; - debug!(self.log, "Loaded roles: {:?}", roles); + // In case the above didn't error, commit. + txn.commit(); + Ok(()) + } + fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> { + let roles = Role::load_file(path)?; - Ok(()) - } + for (k,v) in roles.iter() { + self.put_role(txn, k, v.clone())?; + } + + debug!(self.log, "Loaded roles: {:?}", roles); + + Ok(()) + } } impl RoleDB for Internal { @@ -148,7 +159,7 @@ impl RoleDB for Internal { self._get_role(&txn, roleID) } - fn tally_role(&self, roles: &mut HashSet, roleID: &RoleIdentifier) -> Result<()> { + fn tally_role(&self, roles: &mut HashMap, roleID: &RoleIdentifier) -> Result<()> { let txn = self.env.begin_ro_txn()?; self._tally_role(&txn, roles, roleID) } diff --git a/src/initiator.rs b/src/initiator.rs index 729d5ae..f2c4494 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -31,6 +31,7 @@ pub trait Sensor { type BoxSensor = Box; pub struct Initiator { + log: Logger, signal: MutableSignalCloned>, machine: Option, future: Option, MachineState)>>, @@ -41,8 +42,9 @@ pub struct Initiator { } impl Initiator { - pub fn new(sensor: BoxSensor, signal: MutableSignalCloned>) -> Self { + pub fn new(log: Logger, sensor: BoxSensor, signal: MutableSignalCloned>) -> Self { Self { + log: log, signal: signal, machine: None, future: None, @@ -52,11 +54,11 @@ impl Initiator { } } - pub fn wrap(sensor: BoxSensor) -> (Mutable>, Self) { + pub fn wrap(log: Logger, sensor: BoxSensor) -> (Mutable>, Self) { let m = Mutable::new(None); let s = m.signal_cloned(); - (m, Self::new(sensor, s)) + (m, Self::new(log, sensor, s)) } } @@ -71,22 +73,30 @@ impl Future for Initiator { Poll::Pending => { } Poll::Ready(None) => return Poll::Ready(()), // Keep in mind this is actually an Option - Poll::Ready(Some(machine)) => this.machine = machine, + Poll::Ready(Some(machine)) => { + + match machine.as_ref().map(|m| m.try_lock()) { + None => info!(this.log, "Deinstalled machine"), + Some(None) => info!(this.log, "Installed new machine with locked mutex!"), + Some(Some(g)) => info!(this.log, "Installed new machine {}", g.id), + } + + this.machine = machine; + }, } // Do as much work as we can: loop { // Always poll the state change future first if let Some(ref mut f) = this.state_change_fut { - print!("Polling state change fut ..."); match Future::poll(Pin::new(f), cx) { // If there is a state change future and it would block we return early Poll::Pending => { - println!(" blocked"); + debug!(this.log, "State change blocked"); return Poll::Pending; }, Poll::Ready(Ok(tok)) => { - println!(" returned ok"); + debug!(this.log, "State change returned ok"); // Explicity drop the future let _ = this.state_change_fut.take(); @@ -94,7 +104,7 @@ impl Future for Initiator { this.token.replace(tok); } Poll::Ready(Err(e)) => { - println!(" returned err: {:?}", e); + info!(this.log, "State change returned err: {}", e); // Explicity drop the future let _ = this.state_change_fut.take(); } @@ -107,7 +117,7 @@ impl Future for Initiator { this.future = Some(this.sensor.run_sensor()); }, Some(Poll::Ready((user, state))) => { - println!("New sensor fut"); + 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) @@ -132,7 +142,7 @@ pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(Init let mut v = Vec::new(); for (name, initiator) in initiators { - let (m, i) = Initiator::wrap(initiator); + let (m, i) = Initiator::wrap(log.new(o!("name" => name.clone())), initiator); map.insert(name.clone(), m); v.push(i); } @@ -187,7 +197,7 @@ impl Sensor for Dummy { } else { let user = User::new( UserId::new("test".to_string(), None, None), - UserData::new(vec![], 0), + 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))); diff --git a/src/main.rs b/src/main.rs index 5773601..0673956 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,7 +128,9 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { debug!(log, "Loaded Config: {:?}", config); if matches.is_present("dump") { - error!(log, "Dumping is currently not implemented"); + let db = db::Databases::new(&log, &config)?; + let v = db.access.dump_roles(); + info!(log, "Roles {:?}", v); Ok(()) } else if matches.is_present("load") { let db = db::Databases::new(&log, &config)?;