mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-22 14:57:56 +01:00
Roles dumping
This commit is contained in:
parent
a77841edce
commit
fbc661f478
@ -60,6 +60,10 @@ impl AccessControl {
|
|||||||
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> {
|
||||||
|
self.internal.dump_roles()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for AccessControl {
|
impl fmt::Debug for AccessControl {
|
||||||
@ -90,13 +94,13 @@ pub trait RoleDB {
|
|||||||
/// implementations.
|
/// implementations.
|
||||||
fn check_roles(&self, roles: &[RoleIdentifier], perm: &Permission) -> Result<bool> {
|
fn check_roles(&self, roles: &[RoleIdentifier], perm: &Permission) -> Result<bool> {
|
||||||
// Tally all roles. Makes dependent roles easier
|
// Tally all roles. Makes dependent roles easier
|
||||||
let mut roleset = HashSet::new();
|
let mut roleset = HashMap::new();
|
||||||
for roleID in roles {
|
for roleID in roles {
|
||||||
self.tally_role(&mut roleset, roleID)?;
|
self.tally_role(&mut roleset, roleID)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter all unique role->permissions we've found and early return on match.
|
// 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() {
|
for perm_rule in role.permissions.iter() {
|
||||||
if perm_rule.match_perm(&perm) {
|
if perm_rule.match_perm(&perm) {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@ -111,16 +115,16 @@ pub trait RoleDB {
|
|||||||
///
|
///
|
||||||
/// A Default implementation exists which adapter may overwrite with more efficient
|
/// A Default implementation exists which adapter may overwrite with more efficient
|
||||||
/// implementations.
|
/// implementations.
|
||||||
fn tally_role(&self, roles: &mut HashSet<Role>, roleID: &RoleIdentifier) -> Result<()> {
|
fn tally_role(&self, roles: &mut HashMap<RoleIdentifier, Role>, roleID: &RoleIdentifier) -> Result<()> {
|
||||||
if let Some(role) = self.get_role(roleID)? {
|
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
|
// Only check and tally parents of a role at the role itself if it's the first time we
|
||||||
// see it
|
// see it
|
||||||
if !roles.contains(&role) {
|
if !roles.contains_key(&roleID) {
|
||||||
for parent in role.parents.iter() {
|
for parent in role.parents.iter() {
|
||||||
self.tally_role(roles, parent)?;
|
self.tally_role(roles, parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
roles.insert(role);
|
roles.insert(roleID.clone(), role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +375,7 @@ impl fmt::Display for PermissionBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
/// A borrowed permission string
|
/// A borrowed permission string
|
||||||
///
|
///
|
||||||
/// Permissions have total equality and partial ordering.
|
/// Permissions have total equality and partial ordering.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ use flexbuffers;
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
use lmdb::{Environment, Transaction, RwTransaction, Cursor, Iter};
|
||||||
|
|
||||||
use crate::config::Settings;
|
use crate::config::Settings;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
@ -36,36 +36,45 @@ impl Internal {
|
|||||||
pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &UserData, perm: &P)
|
pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &UserData, perm: &P)
|
||||||
-> Result<bool>
|
-> Result<bool>
|
||||||
{
|
{
|
||||||
|
debug!(self.log, "Checking user {:?} for permission {:?}", user, perm.as_ref());
|
||||||
// Tally all roles. Makes dependent roles easier
|
// Tally all roles. Makes dependent roles easier
|
||||||
let mut roles = HashSet::new();
|
let mut roles = HashMap::new();
|
||||||
for roleID in user.roles.iter() {
|
for roleID in user.roles.iter() {
|
||||||
|
debug!(self.log, "Tallying role {} for its parents", roleID);
|
||||||
self._tally_role(txn, &mut roles, roleID)?;
|
self._tally_role(txn, &mut roles, roleID)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter all unique role->permissions we've found and early return on match.
|
// Iter all unique role->permissions we've found and early return on match.
|
||||||
// TODO: Change this for negative permissions?
|
// 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() {
|
for perm_rule in role.permissions.iter() {
|
||||||
if perm_rule.match_perm(perm) {
|
if perm_rule.match_perm(perm) {
|
||||||
|
debug!(self.log, " matches permission rule {}", perm_rule);
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
trace!(self.log, " rejecting permission rule {}", perm_rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!(self.log, "Checked all roles, rejecting access");
|
||||||
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _tally_role<T: Transaction>(&self, txn: &T, roles: &mut HashSet<Role>, roleID: &RoleIdentifier) -> Result<()> {
|
fn _tally_role<T: Transaction>(&self, txn: &T, roles: &mut HashMap<RoleIdentifier, Role>, roleID: &RoleIdentifier) -> Result<()> {
|
||||||
if let Some(role) = self._get_role(txn, roleID)? {
|
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
|
// Only check and tally parents of a role at the role itself if it's the first time we
|
||||||
// see it
|
// see it
|
||||||
if !roles.contains(&role) {
|
if !roles.contains_key(&roleID) {
|
||||||
for parent in role.parents.iter() {
|
for parent in role.parents.iter() {
|
||||||
self._tally_role(txn, roles, parent)?;
|
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(())
|
Ok(())
|
||||||
@ -90,35 +99,37 @@ impl Internal {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_db<T: Transaction>(&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<Vec<(RoleIdentifier, Role)>> {
|
||||||
|
let txn = self.env.begin_ro_txn()?;
|
||||||
|
self.dump_roles_txn(&txn)
|
||||||
|
}
|
||||||
|
pub fn dump_roles_txn<T: Transaction>(&self, txn: &T) -> Result<Vec<(RoleIdentifier, Role)>> {
|
||||||
|
let mut cursor = txn.open_ro_cursor(self.roledb)?;
|
||||||
|
|
||||||
|
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::<RoleIdentifier>().unwrap();
|
||||||
|
let role = flexbuffers::from_slice(v)?;
|
||||||
|
vec.push((role_id, role));
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_roles<T: Transaction>(&mut self, txn: &T, mut path: PathBuf) -> Result<()> {
|
Ok(vec)
|
||||||
// TODO implement this for the new format
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_roles<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
pub fn load_roles<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
let mut txn = self.env.begin_rw_txn()?;
|
let mut txn = self.env.begin_rw_txn()?;
|
||||||
self.load_roles_txn(&mut txn, path.as_ref())
|
self.load_roles_txn(&mut txn, path.as_ref())?;
|
||||||
|
|
||||||
|
// In case the above didn't error, commit.
|
||||||
|
txn.commit();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> {
|
fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> {
|
||||||
let roles = Role::load_file(path)?;
|
let roles = Role::load_file(path)?;
|
||||||
@ -148,7 +159,7 @@ impl RoleDB for Internal {
|
|||||||
self._get_role(&txn, roleID)
|
self._get_role(&txn, roleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tally_role(&self, roles: &mut HashSet<Role>, roleID: &RoleIdentifier) -> Result<()> {
|
fn tally_role(&self, roles: &mut HashMap<RoleIdentifier, Role>, roleID: &RoleIdentifier) -> Result<()> {
|
||||||
let txn = self.env.begin_ro_txn()?;
|
let txn = self.env.begin_ro_txn()?;
|
||||||
self._tally_role(&txn, roles, roleID)
|
self._tally_role(&txn, roles, roleID)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ pub trait Sensor {
|
|||||||
type BoxSensor = Box<dyn Sensor + Send>;
|
type BoxSensor = Box<dyn Sensor + Send>;
|
||||||
|
|
||||||
pub struct Initiator {
|
pub struct Initiator {
|
||||||
|
log: Logger,
|
||||||
signal: MutableSignalCloned<Option<Machine>>,
|
signal: MutableSignalCloned<Option<Machine>>,
|
||||||
machine: Option<Machine>,
|
machine: Option<Machine>,
|
||||||
future: Option<BoxFuture<'static, (Option<User>, MachineState)>>,
|
future: Option<BoxFuture<'static, (Option<User>, MachineState)>>,
|
||||||
@ -41,8 +42,9 @@ pub struct Initiator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Initiator {
|
impl Initiator {
|
||||||
pub fn new(sensor: BoxSensor, signal: MutableSignalCloned<Option<Machine>>) -> Self {
|
pub fn new(log: Logger, sensor: BoxSensor, signal: MutableSignalCloned<Option<Machine>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
log: log,
|
||||||
signal: signal,
|
signal: signal,
|
||||||
machine: None,
|
machine: None,
|
||||||
future: None,
|
future: None,
|
||||||
@ -52,11 +54,11 @@ impl Initiator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(sensor: BoxSensor) -> (Mutable<Option<Machine>>, Self) {
|
pub fn wrap(log: Logger, sensor: BoxSensor) -> (Mutable<Option<Machine>>, Self) {
|
||||||
let m = Mutable::new(None);
|
let m = Mutable::new(None);
|
||||||
let s = m.signal_cloned();
|
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::Pending => { }
|
||||||
Poll::Ready(None) => return Poll::Ready(()),
|
Poll::Ready(None) => return Poll::Ready(()),
|
||||||
// Keep in mind this is actually an Option<Machine>
|
// Keep in mind this is actually an Option<Machine>
|
||||||
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:
|
// Do as much work as we can:
|
||||||
loop {
|
loop {
|
||||||
// Always poll the state change future first
|
// Always poll the state change future first
|
||||||
if let Some(ref mut f) = this.state_change_fut {
|
if let Some(ref mut f) = this.state_change_fut {
|
||||||
print!("Polling state change fut ...");
|
|
||||||
match Future::poll(Pin::new(f), cx) {
|
match Future::poll(Pin::new(f), cx) {
|
||||||
// If there is a state change future and it would block we return early
|
// If there is a state change future and it would block we return early
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
println!(" blocked");
|
debug!(this.log, "State change blocked");
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
},
|
},
|
||||||
Poll::Ready(Ok(tok)) => {
|
Poll::Ready(Ok(tok)) => {
|
||||||
println!(" returned ok");
|
debug!(this.log, "State change returned ok");
|
||||||
// Explicity drop the future
|
// Explicity drop the future
|
||||||
let _ = this.state_change_fut.take();
|
let _ = this.state_change_fut.take();
|
||||||
|
|
||||||
@ -94,7 +104,7 @@ impl Future for Initiator {
|
|||||||
this.token.replace(tok);
|
this.token.replace(tok);
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(e)) => {
|
Poll::Ready(Err(e)) => {
|
||||||
println!(" returned err: {:?}", e);
|
info!(this.log, "State change returned err: {}", e);
|
||||||
// Explicity drop the future
|
// Explicity drop the future
|
||||||
let _ = this.state_change_fut.take();
|
let _ = this.state_change_fut.take();
|
||||||
}
|
}
|
||||||
@ -107,7 +117,7 @@ impl Future for Initiator {
|
|||||||
this.future = Some(this.sensor.run_sensor());
|
this.future = Some(this.sensor.run_sensor());
|
||||||
},
|
},
|
||||||
Some(Poll::Ready((user, state))) => {
|
Some(Poll::Ready((user, state))) => {
|
||||||
println!("New sensor fut");
|
debug!(this.log, "Sensor returned a new state");
|
||||||
this.future.take();
|
this.future.take();
|
||||||
let f = this.machine.as_mut().map(|machine| {
|
let f = this.machine.as_mut().map(|machine| {
|
||||||
machine.request_state_change(user.as_ref(), state)
|
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();
|
let mut v = Vec::new();
|
||||||
for (name, initiator) in initiators {
|
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);
|
map.insert(name.clone(), m);
|
||||||
v.push(i);
|
v.push(i);
|
||||||
}
|
}
|
||||||
@ -187,7 +197,7 @@ impl Sensor for Dummy {
|
|||||||
} else {
|
} else {
|
||||||
let user = User::new(
|
let user = User::new(
|
||||||
UserId::new("test".to_string(), None, None),
|
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();
|
let id = user.id.clone();
|
||||||
return (Some(user), MachineState::used(Some(id)));
|
return (Some(user), MachineState::used(Some(id)));
|
||||||
|
@ -128,7 +128,9 @@ fn maybe(matches: clap::ArgMatches, log: Arc<Logger>) -> Result<(), Error> {
|
|||||||
debug!(log, "Loaded Config: {:?}", config);
|
debug!(log, "Loaded Config: {:?}", config);
|
||||||
|
|
||||||
if matches.is_present("dump") {
|
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(())
|
Ok(())
|
||||||
} else if matches.is_present("load") {
|
} else if matches.is_present("load") {
|
||||||
let db = db::Databases::new(&log, &config)?;
|
let db = db::Databases::new(&log, &config)?;
|
||||||
|
Loading…
Reference in New Issue
Block a user