mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-10 17:43:23 +01:00
Adds --load/--dump. Role dumping implemented already
These mutually exclusive flags allow dumping and loading of database contents in a textual format
This commit is contained in:
parent
5f78685fb2
commit
b8559e1d64
127
src/access.rs
127
src/access.rs
@ -3,11 +3,17 @@
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use flexbuffers;
|
use flexbuffers;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
use lmdb::{Transaction, RoTransaction, RwTransaction};
|
use lmdb::{Transaction, RwTransaction, Cursor};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
@ -118,6 +124,125 @@ impl PermissionsProvider {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dump_db<T: Transaction>(&mut self, txn: &T, mut path: PathBuf) -> Result<()> {
|
||||||
|
path.push("roles");
|
||||||
|
fs::create_dir(&path);
|
||||||
|
// Rust's stdlib considers the last element the file name 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();
|
||||||
|
|
||||||
|
let mut perm_cursor = txn.open_ro_cursor(self.permdb)?;
|
||||||
|
info!(self.log, "================: PERMS :====================");
|
||||||
|
for perm in perm_cursor.iter_start() {
|
||||||
|
info!(self.log, "{:?}", perm)
|
||||||
|
}
|
||||||
|
info!(self.log, "=============================================");
|
||||||
|
|
||||||
|
let mut user_cursor = txn.open_ro_cursor(self.userdb)?;
|
||||||
|
info!(self.log, "================: USERS :====================");
|
||||||
|
for user in user_cursor.iter_start() {
|
||||||
|
info!(self.log, "{:?}", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_roles<T: Transaction>(&mut self, txn: &T, mut path: PathBuf) -> Result<()> {
|
||||||
|
let mut role_cursor = txn.open_ro_cursor(self.roledb)?;
|
||||||
|
for buf in role_cursor.iter_start() {
|
||||||
|
let (kbuf, vbuf) = buf?;
|
||||||
|
let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>());
|
||||||
|
let roleID = u64::from_ne_bytes(kbytes.try_into().unwrap());
|
||||||
|
let role: Role = flexbuffers::from_slice(vbuf)?;
|
||||||
|
let filename = format!("{:x}.toml", roleID);
|
||||||
|
path.set_file_name(filename);
|
||||||
|
let mut fp = std::fs::File::create(&path)?;
|
||||||
|
let toml = toml::to_vec(&role)?;
|
||||||
|
fp.write_all(&toml);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_db(&mut self, txn: &mut RwTransaction, mut path: PathBuf) -> Result<()> {
|
||||||
|
// ====================: ROLES :====================
|
||||||
|
path.push("roles");
|
||||||
|
if !path.is_dir() {
|
||||||
|
error!(self.log, "Given load directory is malformed, no 'roles' subdir, not loading roles!");
|
||||||
|
} else {
|
||||||
|
self.load_roles(txn, path.as_path())?;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
// ====================: PERMS :====================
|
||||||
|
path.push("perms");
|
||||||
|
if !path.is_dir() {
|
||||||
|
error!(self.log, "Given load directory is malformed, no 'perms' subdir, not loading perms!");
|
||||||
|
} else {
|
||||||
|
//self.load_perms(txn, &path)?;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
// ====================: USERS :====================
|
||||||
|
path.push("users");
|
||||||
|
if !path.is_dir() {
|
||||||
|
error!(self.log, "Given load directory is malformed, no 'users' subdir, not loading users!");
|
||||||
|
} else {
|
||||||
|
//self.load_users(txn, &path)?;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
// =================================================
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_roles(&mut self, txn: &mut RwTransaction, path: &Path) -> Result<()> {
|
||||||
|
for entry in std::fs::read_dir(path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_file() {
|
||||||
|
// will only ever be none if the path has no file name and then how is it a file?!
|
||||||
|
let roleID_str = path
|
||||||
|
.file_stem().expect("Found a file with no filename?")
|
||||||
|
.to_str().expect("Found an OsStr that isn't valid Unicode. Fix your OS!");
|
||||||
|
let roleID = match u64::from_str_radix(roleID_str, 16) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(self.log, "File {} had a invalid name. Expected an u64 in [0-9a-z] hex with optional file ending: {}. Skipping!", path.display(), e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let s = match fs::read_to_string(path.as_path()) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(self.log, "Failed to open file {}: {}, skipping!"
|
||||||
|
, path.display()
|
||||||
|
, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let role: Role = match toml::from_str(&s) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(self.log, "Failed to parse role at path {}: {}, skipping!"
|
||||||
|
, path.display()
|
||||||
|
, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.put_role(txn, roleID, role)?;
|
||||||
|
debug!(self.log, "Loaded role {}", roleID);
|
||||||
|
} else {
|
||||||
|
warn!(self.log, "Path {} is not a file, skipping!", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This line documents init
|
/// This line documents init
|
||||||
|
65
src/main.rs
65
src/main.rs
@ -33,6 +33,8 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use lmdb::Transaction;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
|
||||||
const LMDB_MAX_DB: u32 = 16;
|
const LMDB_MAX_DB: u32 = 16;
|
||||||
@ -63,6 +65,18 @@ fn main() -> Result<(), Error> {
|
|||||||
.help("Print a default config to stdout instead of running")
|
.help("Print a default config to stdout instead of running")
|
||||||
.long("print-default")
|
.long("print-default")
|
||||||
)
|
)
|
||||||
|
.arg(Arg::with_name("dump")
|
||||||
|
.help("Dump all databases into the given directory")
|
||||||
|
.long("dump")
|
||||||
|
.conflicts_with("load")
|
||||||
|
.takes_value(true)
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("load")
|
||||||
|
.help("Load databases from the given directory")
|
||||||
|
.long("load")
|
||||||
|
.conflicts_with("dump")
|
||||||
|
.takes_value(true)
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
// Check for the --print-default option first because we don't need to do anything else in that
|
// Check for the --print-default option first because we don't need to do anything else in that
|
||||||
@ -81,6 +95,7 @@ fn main() -> Result<(), Error> {
|
|||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If no `config` option is given use a preset default.
|
// If no `config` option is given use a preset default.
|
||||||
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.toml");
|
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.toml");
|
||||||
let config = config::read(&PathBuf::from_str(configpath).unwrap())?;
|
let config = config::read(&PathBuf::from_str(configpath).unwrap())?;
|
||||||
@ -92,11 +107,6 @@ fn main() -> Result<(), Error> {
|
|||||||
let log = Arc::new(log::init(&config));
|
let log = Arc::new(log::init(&config));
|
||||||
info!(log, "Starting");
|
info!(log, "Starting");
|
||||||
|
|
||||||
// Kick up an executor
|
|
||||||
// Most initializations from now on do some amount of IO and are much better done in an
|
|
||||||
// asyncronous fashion.
|
|
||||||
let mut exec = LocalPool::new();
|
|
||||||
|
|
||||||
// Initialize the LMDB environment. Since this would usually block untill the mmap() finishes
|
// Initialize the LMDB environment. Since this would usually block untill the mmap() finishes
|
||||||
// we wrap it in smol::unblock which runs this as future in a different thread.
|
// we wrap it in smol::unblock which runs this as future in a different thread.
|
||||||
let e_config = config.clone();
|
let e_config = config.clone();
|
||||||
@ -106,6 +116,12 @@ fn main() -> Result<(), Error> {
|
|||||||
.set_max_dbs(LMDB_MAX_DB as libc::c_uint)
|
.set_max_dbs(LMDB_MAX_DB as libc::c_uint)
|
||||||
.open(&PathBuf::from_str("/tmp/a.db").unwrap())?;
|
.open(&PathBuf::from_str("/tmp/a.db").unwrap())?;
|
||||||
|
|
||||||
|
// Kick up an executor
|
||||||
|
// Most initializations from now on do some amount of IO and are much better done in an
|
||||||
|
// asyncronous fashion.
|
||||||
|
let mut exec = LocalPool::new();
|
||||||
|
|
||||||
|
|
||||||
// Start loading the machine database, authentication system and permission system
|
// Start loading the machine database, authentication system and permission system
|
||||||
// All of those get a custom logger so the source of a log message can be better traced and
|
// All of those get a custom logger so the source of a log message can be better traced and
|
||||||
// filtered
|
// filtered
|
||||||
@ -113,6 +129,45 @@ fn main() -> Result<(), Error> {
|
|||||||
let pdb = access::init(log.new(o!("system" => "permissions")), &config, &env);
|
let pdb = access::init(log.new(o!("system" => "permissions")), &config, &env);
|
||||||
let authentication_f = auth::init(log.new(o!("system" => "authentication")), config.clone());
|
let authentication_f = auth::init(log.new(o!("system" => "authentication")), config.clone());
|
||||||
|
|
||||||
|
// If --load or --dump is given we can stop at this point and load/dump the database and then
|
||||||
|
// exit.
|
||||||
|
if matches.is_present("load") {
|
||||||
|
if let Some(pathstr) = matches.value_of("load") {
|
||||||
|
let path = std::path::Path::new(pathstr);
|
||||||
|
if !path.is_dir() {
|
||||||
|
error!(log, "The provided path is not a directory or does not exist");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut txn = env.begin_rw_txn()?;
|
||||||
|
let path = path.to_path_buf();
|
||||||
|
pdb?.load_db(&mut txn, path)?;
|
||||||
|
txn.commit();
|
||||||
|
} else {
|
||||||
|
error!(log, "You must provide a directory path to load from");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(())
|
||||||
|
} else if matches.is_present("dump") {
|
||||||
|
if let Some(pathstr) = matches.value_of("dump") {
|
||||||
|
let path = std::path::Path::new(pathstr);
|
||||||
|
if let Err(e) = std::fs::create_dir_all(path) {
|
||||||
|
error!(log, "The provided path could not be created: {}", e);
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let txn = env.begin_ro_txn()?;
|
||||||
|
let path = path.to_path_buf();
|
||||||
|
pdb?.dump_db(&txn, path)?;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
error!(log, "You must provide a directory path to dump into");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Bind to each address in config.listen.
|
// Bind to each address in config.listen.
|
||||||
// This is a Stream over Futures so it will do absolutely nothing unless polled to completion
|
// This is a Stream over Futures so it will do absolutely nothing unless polled to completion
|
||||||
let listeners_s: futures::stream::Collect<_, Vec<TcpListener>>
|
let listeners_s: futures::stream::Collect<_, Vec<TcpListener>>
|
||||||
|
Loading…
Reference in New Issue
Block a user