Update config.rs to compile

This commit is contained in:
Nadja Reitzenstein 2021-10-20 12:58:05 +02:00
parent 80b6807f21
commit 541f8585c0
8 changed files with 152 additions and 116 deletions

View File

@ -9,9 +9,13 @@
, init_connections = [] : List { _1 : Text, _2 : Text } , init_connections = [] : List { _1 : Text, _2 : Text }
, initiators = ./initiators.dhall , initiators = ./initiators.dhall
, listens = , listens =
[ { address = "127.0.0.1", port = Some 59661 } [ "127.0.0.1"
, { address = "::1", port = Some 59661 } , "::1"
, { address = "192.168.0.114", port = Some 59661 } , "[::1]:1235"
, "localhost:1234"
, "localhost"
, "notahost:541"
, "notahostandnoport"
] ]
, machines = ./machines.dhall , machines = ./machines.dhall
, db_path = "/tmp/bffh" , db_path = "/tmp/bffh"

View File

@ -2,12 +2,14 @@ use std::{
io, io,
io::Write, io::Write,
path::PathBuf, path::PathBuf,
sync::Arc,
}; };
use clap::{App, Arg, crate_version, crate_description, crate_name}; use clap::{App, Arg, crate_version, crate_description, crate_name};
use std::str::FromStr; 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 // Argument parsing
// values for the name, description and version are pulled from `Cargo.toml`. // values for the name, description and version are pulled from `Cargo.toml`.
let matches = App::new(crate_name!()) 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") { } else if matches.is_present("check config") {
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall"); let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall");
match config::read(&PathBuf::from_str(configpath).unwrap()) { match config::read(&PathBuf::from_str(configpath).unwrap()) {
Ok(cfg) => { Ok(_) => {
//TODO: print a normalized version of the supplied config //TODO: print a normalized version of the supplied config
println!("config is valid"); println!("config is valid");
std::process::exit(0); 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. // If no `config` option is given use a preset default.
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall"); let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall");
let config = config::read(&PathBuf::from_str(configpath).unwrap())?; 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") { if matches.is_present("dump") {
let db = db::Databases::new(&log, &config)?; let db = db::Databases::new(&log, &config)?;
let v = db.access.dump_roles().unwrap(); 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) server::serve_api_connections(log.clone(), config, db, network, ex)
} }
*/
Ok(())
} }
fn main() { fn main() {

View File

@ -2,10 +2,14 @@ use std::default::Default;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize, Deserializer, Serializer};
use crate::error::Result; 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> { pub fn read(path: &Path) -> Result<Config> {
serde_dhall::from_file(path) 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 // 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 (=> // it contains enough information to open a socket (i.e. it checks if it's a valid path (=>
// Unix socket) or IPv4/v6 address) // Unix socket) or IPv4/v6 address)
pub listens: Box<[Listen]>, pub listens: Vec<Listen>,
/// Machine descriptions to load /// Machine descriptions to load
pub machines: HashMap<MachineIdentifier, MachineDescription>, //pub machines: HashMap<MachineIdentifier, MachineDescription>,
/// Actors to load and their configuration options /// Actors to load and their configuration options
pub actors: HashMap<String, ModuleConfig>, pub actors: HashMap<String, ModuleConfig>,
@ -48,23 +52,107 @@ pub struct RoleConfig {
pub permissions: Vec<PermRule>, pub permissions: Vec<PermRule>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Listen {
pub address: String,
pub port: Option<u16>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleConfig { pub struct ModuleConfig {
pub module: String, pub module: String,
pub params: HashMap<String, 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 { impl Default for Config {
fn default() -> Self { fn default() -> Self {
let mut actors: HashMap::<String, ModuleConfig> = HashMap::new(); let mut actors: HashMap::<String, ModuleConfig> = HashMap::new();
let mut initiators: HashMap::<String, ModuleConfig> = HashMap::new(); let mut initiators: HashMap::<String, ModuleConfig> = HashMap::new();
let mut machines = HashMap::new();
actors.insert("Actor".to_string(), ModuleConfig { actors.insert("Actor".to_string(), ModuleConfig {
module: "Shelly".to_string(), module: "Shelly".to_string(),
@ -75,25 +163,13 @@ impl Default for Config {
params: HashMap::new(), 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 { Config {
listens: Box::new([ listens: vec![
Listen { Listen {
address: "localhost".to_string(), address: "127.0.0.1".to_string(),
port: Some(DEFAULT_PORT), port: None,
} }
]), ],
machines,
actors, actors,
initiators, initiators,
mqtt_url: "tcp://localhost:1883".to_string(), mqtt_url: "tcp://localhost:1883".to_string(),

View File

@ -7,20 +7,13 @@ use std::sync::Arc;
use std::fmt; use std::fmt;
use std::collections::HashMap; use std::collections::HashMap;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::path::Path;
use std::fs;
use std::iter::FromIterator;
use std::convert::{TryFrom, Into}; use std::convert::{TryFrom, Into};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::error::Result; use crate::error::Result;
pub mod internal;
use crate::config::Config; use crate::config::Config;
use crate::db::user::UserData;
pub use internal::Internal;
pub struct AccessControl { pub struct AccessControl {
internal: HashMap<RoleIdentifier, Role>, 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)>> { pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> {
Ok(self.internal.iter().map(|(k,v)| (k.clone(), v.clone())).collect()) Ok(self.internal.iter().map(|(k,v)| (k.clone(), v.clone())).collect())
} }
@ -106,21 +80,6 @@ pub trait RoleDB {
Ok(()) 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> { impl RoleDB for HashMap<RoleIdentifier, Role> {
@ -162,15 +121,6 @@ pub struct Role {
} }
impl 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 { pub fn new(parents: Vec<RoleIdentifier>, permissions: Vec<PermRule>) -> Self {
Self { parents, permissions } Self { parents, permissions }
} }

View File

@ -3,8 +3,6 @@ use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use flexbuffers;
use slog::Logger; use slog::Logger;
use lmdb::{Environment, Transaction, RwTransaction, Cursor}; use lmdb::{Environment, Transaction, RwTransaction, Cursor};
@ -12,7 +10,6 @@ use crate::config::Config;
use crate::error::Result; use crate::error::Result;
use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB}; use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB};
use crate::db::user::UserData;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Internal { pub struct Internal {
@ -31,28 +28,28 @@ 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()); tracing::debug!("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 = HashMap::new(); let mut roles = HashMap::new();
for role_id in user.roles.iter() { 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)?; self._tally_role(txn, &mut roles, role_id)?;
} }
// 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 (roleid, role) in roles.iter() { for (roleid, role) in roles.iter() {
debug!(self.log, " checking role {}", roleid); tracing::debug!(" 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); tracing::debug!(" matches permission rule {}", perm_rule);
return Ok(true); 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); return Ok(false);
} }
@ -69,14 +66,14 @@ impl Internal {
roles.insert(role_id.clone(), role); roles.insert(role_id.clone(), role);
} }
} else { } 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(()) Ok(())
} }
pub fn _get_role<'txn, T: Transaction>(&self, txn: &'txn T, role_id: &RoleIdentifier) -> Result<Option<Role>> { 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()) { match txn.get(self.roledb, &role_id.name.as_bytes()) {
Ok(bytes) => { Ok(bytes) => {
Ok(Some(flexbuffers::from_slice(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()); let role_id = RoleIdentifier::local_from_str("lmdb".to_string(), role_name_str.to_string());
match flexbuffers::from_slice(v) { match flexbuffers::from_slice(v) {
Ok(role) => vec.push((role_id, role)), 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()), Err(e) => return Err(e.into()),
@ -134,7 +132,7 @@ impl Internal {
self.put_role(txn, k, v.clone())?; self.put_role(txn, k, v.clone())?;
} }
debug!(self.log, "Loaded roles: {:?}", roles); tracing::debug!("Loaded roles: {:?}", roles);
Ok(()) Ok(())
} }
@ -154,20 +152,4 @@ impl RoleDB for Internal {
let txn = self.env.begin_ro_txn()?; let txn = self.env.begin_ro_txn()?;
self._tally_role(&txn, roles, role_id) 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))
}

View File

@ -48,6 +48,9 @@ mod resources;
pub use resources::{ pub use resources::{
ResourceDB, ResourceDB,
}; };
pub mod access;
use lmdb::Error; use lmdb::Error;
use rkyv::ser::serializers::AlignedSerializer; use rkyv::ser::serializers::AlignedSerializer;

View File

@ -20,7 +20,8 @@ mod db;
mod network; mod network;
pub mod oid; pub mod oid;
mod varint; mod varint;
mod error; pub mod error;
pub mod config;
/* /*

View File

@ -297,7 +297,7 @@ fn parse_string_child_node(
node_str: &str, node_str: &str,
out: &mut Vec<u8> out: &mut Vec<u8>
) -> Result<(), ObjectIdentifierError> { ) -> Result<(), ObjectIdentifierError> {
let mut node: Node = node_str.parse() let node: Node = node_str.parse()
.map_err(|_| ObjectIdentifierError::IllegalChildNodeValue)?; .map_err(|_| ObjectIdentifierError::IllegalChildNodeValue)?;
// TODO bench against !*node &= 0x80, compiler may already optimize better // TODO bench against !*node &= 0x80, compiler may already optimize better
if node <= 127 { if node <= 127 {