mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-10 17:43:23 +01:00
Update config.rs to compile
This commit is contained in:
parent
80b6807f21
commit
541f8585c0
@ -9,9 +9,13 @@
|
||||
, init_connections = [] : List { _1 : Text, _2 : Text }
|
||||
, initiators = ./initiators.dhall
|
||||
, listens =
|
||||
[ { address = "127.0.0.1", port = Some 59661 }
|
||||
, { address = "::1", port = Some 59661 }
|
||||
, { address = "192.168.0.114", port = Some 59661 }
|
||||
[ "127.0.0.1"
|
||||
, "::1"
|
||||
, "[::1]:1235"
|
||||
, "localhost:1234"
|
||||
, "localhost"
|
||||
, "notahost:541"
|
||||
, "notahostandnoport"
|
||||
]
|
||||
, machines = ./machines.dhall
|
||||
, db_path = "/tmp/bffh"
|
||||
|
@ -2,12 +2,14 @@ use std::{
|
||||
io,
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use clap::{App, Arg, crate_version, crate_description, crate_name};
|
||||
use std::str::FromStr;
|
||||
use diflouroborane::{config, error::Error};
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::error::Error as _;
|
||||
|
||||
fn main_res() -> Result<(), dyn std::error::Error> {
|
||||
fn main_res() -> Result<(), Error> {
|
||||
// Argument parsing
|
||||
// values for the name, description and version are pulled from `Cargo.toml`.
|
||||
let matches = App::new(crate_name!())
|
||||
@ -56,7 +58,7 @@ fn main_res() -> Result<(), dyn std::error::Error> {
|
||||
} else if matches.is_present("check config") {
|
||||
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall");
|
||||
match config::read(&PathBuf::from_str(configpath).unwrap()) {
|
||||
Ok(cfg) => {
|
||||
Ok(_) => {
|
||||
//TODO: print a normalized version of the supplied config
|
||||
println!("config is valid");
|
||||
std::process::exit(0);
|
||||
@ -71,8 +73,23 @@ fn main_res() -> Result<(), dyn std::error::Error> {
|
||||
// If no `config` option is given use a preset default.
|
||||
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall");
|
||||
let config = config::read(&PathBuf::from_str(configpath).unwrap())?;
|
||||
tracing::debug!("Loaded Config: {:?}", config);
|
||||
println!("{:#?}", config);
|
||||
|
||||
let mut sockaddrs = Vec::new();
|
||||
for listen in config.listens {
|
||||
match listen.to_socket_addrs() {
|
||||
Ok(addrs) => {
|
||||
sockaddrs.extend(addrs)
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Invalid listen \"{}\" {}", listen, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Final listens: {:?}", sockaddrs);
|
||||
|
||||
/*
|
||||
if matches.is_present("dump") {
|
||||
let db = db::Databases::new(&log, &config)?;
|
||||
let v = db.access.dump_roles().unwrap();
|
||||
@ -131,6 +148,9 @@ fn main_res() -> Result<(), dyn std::error::Error> {
|
||||
|
||||
server::serve_api_connections(log.clone(), config, db, network, ex)
|
||||
}
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
130
src/config.rs
130
src/config.rs
@ -2,10 +2,14 @@ use std::default::Default;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Serialize, Deserialize, Deserializer, Serializer};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::machine::MachineDescription;
|
||||
use std::fmt::Formatter;
|
||||
use std::net::{SocketAddr, IpAddr, ToSocketAddrs};
|
||||
use std::str::FromStr;
|
||||
use crate::db::access::PermRule;
|
||||
use serde::de::Error;
|
||||
|
||||
pub fn read(path: &Path) -> Result<Config> {
|
||||
serde_dhall::from_file(path)
|
||||
@ -19,10 +23,10 @@ pub struct Config {
|
||||
// TODO: This should really be a variant type; that is something that can figure out itself if
|
||||
// it contains enough information to open a socket (i.e. it checks if it's a valid path (=>
|
||||
// Unix socket) or IPv4/v6 address)
|
||||
pub listens: Box<[Listen]>,
|
||||
pub listens: Vec<Listen>,
|
||||
|
||||
/// Machine descriptions to load
|
||||
pub machines: HashMap<MachineIdentifier, MachineDescription>,
|
||||
//pub machines: HashMap<MachineIdentifier, MachineDescription>,
|
||||
|
||||
/// Actors to load and their configuration options
|
||||
pub actors: HashMap<String, ModuleConfig>,
|
||||
@ -48,23 +52,107 @@ pub struct RoleConfig {
|
||||
pub permissions: Vec<PermRule>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Listen {
|
||||
pub address: String,
|
||||
pub port: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModuleConfig {
|
||||
pub module: String,
|
||||
pub params: HashMap<String, String>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Listen {
|
||||
address: String,
|
||||
port: Option<u16>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Listen {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", &self.address, self.port.unwrap_or(DEFAULT_PORT))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSocketAddrs for Listen {
|
||||
type Iter = <(String, u16) as ToSocketAddrs>::Iter;
|
||||
|
||||
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
|
||||
if let Some(port) = self.port {
|
||||
(self.address.as_str(), port).to_socket_addrs()
|
||||
} else {
|
||||
(self.address.as_str(), DEFAULT_PORT).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Listen {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
deserializer.deserialize_str(ListenVisitor)
|
||||
}
|
||||
}
|
||||
impl serde::Serialize for Listen {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
if let Some(port) = self.port {
|
||||
serializer.serialize_str(&format!("{}:{}", self.address, port))
|
||||
} else {
|
||||
serializer.serialize_str(&self.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ListenVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for ListenVisitor {
|
||||
type Value = Listen;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "A string encoding a valid IP or Hostname (e.g. 127.0.0.1 or [::1]) with\
|
||||
or without a defined port")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
|
||||
where E: Error
|
||||
{
|
||||
let sockaddr = SocketAddr::from_str(v);
|
||||
if let Ok(address) = sockaddr {
|
||||
return Ok(Listen {
|
||||
address: address.ip().to_string(),
|
||||
port: Some(address.port()),
|
||||
})
|
||||
}
|
||||
|
||||
let ipaddr = IpAddr::from_str(v);
|
||||
if let Ok(address) = ipaddr {
|
||||
return Ok(Listen {
|
||||
address: address.to_string(),
|
||||
port: None,
|
||||
})
|
||||
}
|
||||
|
||||
let mut split = v.split(':');
|
||||
let address = split.next()
|
||||
.expect("str::split should always return at least one element")
|
||||
.to_string();
|
||||
let port = if let Some(port) = split.next() {
|
||||
let port: u16 = port.parse()
|
||||
.map_err(|_| {
|
||||
E::custom(&format!("Expected valid ip address or hostname with or without \
|
||||
port. Failed to parse \"{}\".", v))
|
||||
})?;
|
||||
|
||||
Some(port)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Listen { address, port })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let mut actors: HashMap::<String, ModuleConfig> = HashMap::new();
|
||||
let mut initiators: HashMap::<String, ModuleConfig> = HashMap::new();
|
||||
let mut machines = HashMap::new();
|
||||
|
||||
actors.insert("Actor".to_string(), ModuleConfig {
|
||||
module: "Shelly".to_string(),
|
||||
@ -75,25 +163,13 @@ impl Default for Config {
|
||||
params: HashMap::new(),
|
||||
});
|
||||
|
||||
machines.insert("Testmachine".to_string(), MachineDescription {
|
||||
name: "Testmachine".to_string(),
|
||||
description: Some("A test machine".to_string()),
|
||||
privs: PrivilegesBuf {
|
||||
disclose: PermissionBuf::from_string("lab.test.read".to_string()),
|
||||
read: PermissionBuf::from_string("lab.test.read".to_string()),
|
||||
write: PermissionBuf::from_string("lab.test.write".to_string()),
|
||||
manage: PermissionBuf::from_string("lab.test.admin".to_string()),
|
||||
},
|
||||
});
|
||||
|
||||
Config {
|
||||
listens: Box::new([
|
||||
listens: vec![
|
||||
Listen {
|
||||
address: "localhost".to_string(),
|
||||
port: Some(DEFAULT_PORT),
|
||||
address: "127.0.0.1".to_string(),
|
||||
port: None,
|
||||
}
|
||||
]),
|
||||
machines,
|
||||
],
|
||||
actors,
|
||||
initiators,
|
||||
mqtt_url: "tcp://localhost:1883".to_string(),
|
||||
|
@ -7,20 +7,13 @@ use std::sync::Arc;
|
||||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
use std::cmp::Ordering;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::iter::FromIterator;
|
||||
use std::convert::{TryFrom, Into};
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
pub mod internal;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::db::user::UserData;
|
||||
pub use internal::Internal;
|
||||
|
||||
pub struct AccessControl {
|
||||
internal: HashMap<RoleIdentifier, Role>,
|
||||
@ -49,25 +42,6 @@ impl AccessControl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check<P: AsRef<Permission>>(&self, user: &UserData, perm: P) -> Result<bool> {
|
||||
let mut roles = HashMap::new();
|
||||
// Check all user roles by..
|
||||
Ok(user.roles.iter().any(|role| {
|
||||
// 1. Getting the whole tree down to a list of Roles applied
|
||||
self.internal.tally_role(&mut roles, role)?;
|
||||
|
||||
// 2. Checking if any of the roles the user has give any permission granting the
|
||||
// requested one.
|
||||
roles.drain().any(|(rid, role)| {
|
||||
role.permissions.iter().any(|rule| rule.match_perm(perm))
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn collect_permrules(&self, user: &UserData) -> Result<Vec<PermRule>> {
|
||||
self.internal.collect_permrules(user)
|
||||
}
|
||||
|
||||
pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> {
|
||||
Ok(self.internal.iter().map(|(k,v)| (k.clone(), v.clone())).collect())
|
||||
}
|
||||
@ -106,21 +80,6 @@ pub trait RoleDB {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_permrules(&self, user: &UserData) -> Result<Vec<PermRule>> {
|
||||
let mut roleset = HashMap::new();
|
||||
for role_id in user.roles.iter() {
|
||||
self.tally_role(&mut roleset, role_id)?;
|
||||
}
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
// Iter all unique role->permissions we've found and early return on match.
|
||||
for (_roleid, role) in roleset.iter() {
|
||||
output.extend(role.permissions.iter().cloned())
|
||||
}
|
||||
|
||||
return Ok(output);
|
||||
}
|
||||
}
|
||||
|
||||
impl RoleDB for HashMap<RoleIdentifier, Role> {
|
||||
@ -162,15 +121,6 @@ pub struct Role {
|
||||
}
|
||||
|
||||
impl Role {
|
||||
fn load_file<P: AsRef<Path>>(path: P) -> Result<HashMap<RoleIdentifier, Role>> {
|
||||
let content = fs::read(path)?;
|
||||
let file_roles: HashMap<String, Role> = toml::from_slice(&content[..])?;
|
||||
|
||||
Ok(HashMap::from_iter(file_roles.into_iter().map(|(key, value)| {
|
||||
(RoleIdentifier::local_from_str("lmdb".to_string(), key), value)
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn new(parents: Vec<RoleIdentifier>, permissions: Vec<PermRule>) -> Self {
|
||||
Self { parents, permissions }
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use flexbuffers;
|
||||
|
||||
use slog::Logger;
|
||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
||||
|
||||
@ -12,7 +10,6 @@ use crate::config::Config;
|
||||
use crate::error::Result;
|
||||
|
||||
use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB};
|
||||
use crate::db::user::UserData;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Internal {
|
||||
@ -31,28 +28,28 @@ impl Internal {
|
||||
pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &UserData, perm: &P)
|
||||
-> Result<bool>
|
||||
{
|
||||
debug!(self.log, "Checking user {:?} for permission {:?}", user, perm.as_ref());
|
||||
tracing::debug!("Checking user {:?} for permission {:?}", user, perm.as_ref());
|
||||
// Tally all roles. Makes dependent roles easier
|
||||
let mut roles = HashMap::new();
|
||||
for role_id in user.roles.iter() {
|
||||
debug!(self.log, "Tallying role {} for its parents", role_id);
|
||||
tracing::debug!("Tallying role {} for its parents", role_id);
|
||||
self._tally_role(txn, &mut roles, role_id)?;
|
||||
}
|
||||
|
||||
// Iter all unique role->permissions we've found and early return on match.
|
||||
// TODO: Change this for negative permissions?
|
||||
for (roleid, role) in roles.iter() {
|
||||
debug!(self.log, " checking role {}", roleid);
|
||||
tracing::debug!(" checking role {}", roleid);
|
||||
for perm_rule in role.permissions.iter() {
|
||||
if perm_rule.match_perm(perm) {
|
||||
debug!(self.log, " matches permission rule {}", perm_rule);
|
||||
tracing::debug!(" matches permission rule {}", perm_rule);
|
||||
return Ok(true);
|
||||
}
|
||||
trace!(self.log, " rejecting permission rule {}", perm_rule);
|
||||
tracing::trace!(" rejecting permission rule {}", perm_rule);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(self.log, "Checked all roles, rejecting access");
|
||||
tracing::debug!("Checked all roles, rejecting access");
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
@ -69,14 +66,14 @@ impl Internal {
|
||||
roles.insert(role_id.clone(), role);
|
||||
}
|
||||
} else {
|
||||
warn!(self.log, "Did not find role {} while trying to tally", role_id);
|
||||
tracing::warn!("Did not find role {} while trying to tally", role_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn _get_role<'txn, T: Transaction>(&self, txn: &'txn T, role_id: &RoleIdentifier) -> Result<Option<Role>> {
|
||||
debug!(self.log, "Reading role '{}'", role_id.name);
|
||||
tracing::debug!("Reading role '{}'", role_id.name);
|
||||
match txn.get(self.roledb, &role_id.name.as_bytes()) {
|
||||
Ok(bytes) => {
|
||||
Ok(Some(flexbuffers::from_slice(bytes)?))
|
||||
@ -109,7 +106,8 @@ impl Internal {
|
||||
let role_id = RoleIdentifier::local_from_str("lmdb".to_string(), role_name_str.to_string());
|
||||
match flexbuffers::from_slice(v) {
|
||||
Ok(role) => vec.push((role_id, role)),
|
||||
Err(e) => error!(self.log, "Bad format for roleid {}: {}", role_id, e),
|
||||
Err(e) => tracing::error!("Bad format for roleid {}: {}", role_id,
|
||||
e),
|
||||
}
|
||||
},
|
||||
Err(e) => return Err(e.into()),
|
||||
@ -134,7 +132,7 @@ impl Internal {
|
||||
self.put_role(txn, k, v.clone())?;
|
||||
}
|
||||
|
||||
debug!(self.log, "Loaded roles: {:?}", roles);
|
||||
tracing::debug!("Loaded roles: {:?}", roles);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -155,19 +153,3 @@ impl RoleDB for Internal {
|
||||
self._tally_role(&txn, roles, role_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Initialize the access db by loading all the lmdb databases
|
||||
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>)
|
||||
-> std::result::Result<Internal, crate::error::Error>
|
||||
{
|
||||
let mut flags = lmdb::DatabaseFlags::empty();
|
||||
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
|
||||
let roledb = env.create_db(Some("role"), flags)?;
|
||||
debug!(&log, "Opened access database '{}' successfully.", "role");
|
||||
//let permdb = env.create_db(Some("perm"), flags)?;
|
||||
//debug!(&log, "Opened access database '{}' successfully.", "perm");
|
||||
|
||||
Ok(Internal::new(log, env, roledb))
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ mod resources;
|
||||
pub use resources::{
|
||||
ResourceDB,
|
||||
};
|
||||
|
||||
pub mod access;
|
||||
|
||||
use lmdb::Error;
|
||||
use rkyv::ser::serializers::AlignedSerializer;
|
||||
|
||||
|
@ -20,7 +20,8 @@ mod db;
|
||||
mod network;
|
||||
pub mod oid;
|
||||
mod varint;
|
||||
mod error;
|
||||
pub mod error;
|
||||
pub mod config;
|
||||
|
||||
/*
|
||||
|
||||
|
@ -297,7 +297,7 @@ fn parse_string_child_node(
|
||||
node_str: &str,
|
||||
out: &mut Vec<u8>
|
||||
) -> Result<(), ObjectIdentifierError> {
|
||||
let mut node: Node = node_str.parse()
|
||||
let node: Node = node_str.parse()
|
||||
.map_err(|_| ObjectIdentifierError::IllegalChildNodeValue)?;
|
||||
// TODO bench against !*node &= 0x80, compiler may already optimize better
|
||||
if node <= 127 {
|
||||
|
Loading…
Reference in New Issue
Block a user