Stuff to make work

This commit is contained in:
Nadja Reitzenstein 2021-09-19 19:47:29 +02:00
parent abc9126137
commit ba7a59c3de
8 changed files with 125 additions and 26 deletions

View File

@ -39,4 +39,5 @@
} }
} }
, mqtt_url = "tcp://localhost:1883" , mqtt_url = "tcp://localhost:1883"
, db_path = "/tmp/bffh"
} }

View File

@ -13,9 +13,11 @@ use crate::network::Network;
pub mod auth; pub mod auth;
mod machine; mod machine;
mod machines; mod machines;
use machines::Machines; use machines::Machines;
mod users;
use users::Users;
// TODO Session restoration by making the Bootstrap cap a SturdyRef // TODO Session restoration by making the Bootstrap cap a SturdyRef
pub struct Bootstrap { pub struct Bootstrap {
session: Arc<Session>, session: Arc<Session>,
@ -45,13 +47,6 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
Promise::ok(()) Promise::ok(())
} }
fn permission_system(&mut self,
_: PermissionSystemParams,
_: PermissionSystemResults
) -> Promise<(), capnp::Error> {
Promise::ok(())
}
fn machine_system(&mut self, fn machine_system(&mut self,
_: MachineSystemParams, _: MachineSystemParams,
mut res: MachineSystemResults mut res: MachineSystemResults
@ -65,6 +60,11 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
let perms = accessdb.collect_permrules(&user.data) let perms = accessdb.collect_permrules(&user.data)
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))?; .map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))?;
debug!(session.log, "Giving MachineSystem cap to user {} with perms:", user.id);
for r in perms.iter() {
debug!(session.log, " {}", r);
}
// TODO actual permission check and stuff // TODO actual permission check and stuff
// Right now we only check that the user has authenticated at all. // Right now we only check that the user has authenticated at all.
let c = capnp_rpc::new_client(Machines::new(user.id, perms, nw)); let c = capnp_rpc::new_client(Machines::new(user.id, perms, nw));
@ -78,5 +78,31 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
Promise::from_future(f) Promise::from_future(f)
} }
}
fn user_system(
&mut self,
_: UserSystemParams,
mut results: UserSystemResults
) -> Promise<(), capnp::Error> {
let session = self.session.clone();
let accessdb = self.db.access.clone();
let f = async move {
// Ensure the lock is dropped as soon as possible
if let Some(user) = { session.user.lock().await.clone() } {
let perms = accessdb.collect_permrules(&user.data)
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))?;
// TODO actual permission check and stuff
// Right now we only check that the user has authenticated at all.
let c = capnp_rpc::new_client(Users::new(perms));
results.get().set_user_system(c);
}
// Promise is Ok either way, just the machine system may not be set, indicating as
// usual a lack of permission.
Ok(())
};
Promise::from_future(f)
}
}

43
src/api/users.rs Normal file
View File

@ -0,0 +1,43 @@
use capnp::capability::Promise;
use crate::db::access::{PermRule, Permission};
use crate::schema::usersystem_capnp::user_system;
use crate::schema::usersystem_capnp::user_system::{info, manage};
#[derive(Clone, Debug)]
pub struct Users {
perms: Vec<PermRule>,
}
impl Users {
pub fn new(perms: Vec<PermRule>) -> Self {
Self { perms }
}
}
impl user_system::Server for Users {
fn info(
&mut self,
_: user_system::InfoParams,
_: user_system::InfoResults,
) -> Promise<(), capnp::Error> {
Promise::ok(())
}
fn manage(
&mut self,
_: user_system::ManageParams,
mut results: user_system::ManageResults,
) -> Promise<(), capnp::Error> {
let perm: &Permission = Permission::new("bffh.users.manage");
if self.perms.iter().any(|rule| rule.match_perm(perm)) {
results.get().set_manage(capnp_rpc::new_client(self.clone()));
}
Promise::ok(())
}
}
impl info::Server for Users {}
impl manage::Server for Users {}

View File

@ -1,5 +1,5 @@
use std::default::Default; use std::default::Default;
use std::path::Path; use std::path::{Path, PathBuf};
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -36,6 +36,8 @@ pub struct Config {
pub actor_connections: Box<[(String, String)]>, pub actor_connections: Box<[(String, String)]>,
pub init_connections: Box<[(String, String)]>, pub init_connections: Box<[(String, String)]>,
pub db_path: PathBuf,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -83,9 +85,9 @@ impl Default for Config {
port: Some(DEFAULT_PORT), port: Some(DEFAULT_PORT),
} }
]), ]),
machines: machines, machines,
actors: actors, actors,
initiators: initiators, initiators,
mqtt_url: "tcp://localhost:1883".to_string(), mqtt_url: "tcp://localhost:1883".to_string(),
actor_connections: Box::new([ actor_connections: Box::new([
("Testmachine".to_string(), "Actor".to_string()), ("Testmachine".to_string(), "Actor".to_string()),
@ -93,6 +95,8 @@ impl Default for Config {
init_connections: Box::new([ init_connections: Box::new([
("Initiator".to_string(), "Testmachine".to_string()), ("Initiator".to_string(), "Testmachine".to_string()),
]), ]),
db_path: PathBuf::from("/run/bffh/database"),
} }
} }
} }

View File

@ -42,7 +42,7 @@ impl Databases {
let env = lmdb::Environment::new() let env = lmdb::Environment::new()
.set_flags(lmdb::EnvironmentFlags::MAP_ASYNC | lmdb::EnvironmentFlags::NO_SUB_DIR) .set_flags(lmdb::EnvironmentFlags::MAP_ASYNC | lmdb::EnvironmentFlags::NO_SUB_DIR)
.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(config.db_path.as_path())?;
// 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

View File

@ -186,6 +186,31 @@ impl Role {
} }
} }
impl fmt::Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parents:")?;
if self.parents.is_empty() {
writeln!(f, " []")?;
} else {
writeln!(f, "")?;
for p in self.parents.iter() {
writeln!(f, " - {}", p)?;
}
}
write!(f, "permissions:")?;
if self.permissions.is_empty() {
writeln!(f, " []")?;
} else {
writeln!(f, "")?;
for p in self.permissions.iter() {
writeln!(f, " - {}", p)?;
}
}
Ok(())
}
}
type SourceID = String; type SourceID = String;
fn split_once(s: &str, split: char) -> Option<(&str, &str)> { fn split_once(s: &str, split: char) -> Option<(&str, &str)> {
@ -454,7 +479,7 @@ pub enum PermRule {
impl PermRule { impl PermRule {
// Does this rule match that permission // Does this rule match that permission
pub fn match_perm<P: AsRef<Permission>>(&self, perm: &P) -> bool { pub fn match_perm<P: AsRef<Permission> + ?Sized>(&self, perm: &P) -> bool {
match self { match self {
PermRule::Base(ref base) => base.as_permission() == perm.as_ref(), PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() , PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() ,

View File

@ -69,16 +69,15 @@ impl Internal {
roles.insert(role_id.clone(), role); roles.insert(role_id.clone(), role);
} }
} else { } else {
info!(self.log, "Did not find role {} while trying to tally", role_id); warn!(self.log, "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>> {
let string = format!("{}", role_id); debug!(self.log, "Reading role '{}'", role_id.name);
debug!(self.log, "Reading role '{}'", &string); match txn.get(self.roledb, &role_id.name.as_bytes()) {
match txn.get(self.roledb, &string.as_bytes()) {
Ok(bytes) => { Ok(bytes) => {
Ok(Some(flexbuffers::from_slice(bytes)?)) Ok(Some(flexbuffers::from_slice(bytes)?))
}, },
@ -89,8 +88,7 @@ impl Internal {
fn put_role(&self, txn: &mut RwTransaction, role_id: &RoleIdentifier, role: Role) -> Result<()> { fn put_role(&self, txn: &mut RwTransaction, role_id: &RoleIdentifier, role: Role) -> Result<()> {
let bytes = flexbuffers::to_vec(role)?; let bytes = flexbuffers::to_vec(role)?;
let string = format!("{}", role_id); txn.put(self.roledb, &role_id.name.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
txn.put(self.roledb, &string.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
Ok(()) Ok(())
} }
@ -107,8 +105,8 @@ impl Internal {
for r in cursor.iter_start() { for r in cursor.iter_start() {
match r { match r {
Ok( (k,v) ) => { Ok( (k,v) ) => {
let role_id_str = unsafe { std::str::from_utf8_unchecked(k) }; let role_name_str = unsafe { std::str::from_utf8_unchecked(k) };
let role_id = role_id_str.parse::<RoleIdentifier>().unwrap(); let role_id = RoleIdentifier::local_from_str(role_name_str.to_string(), "lmdb".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) => error!(self.log, "Bad format for roleid {}: {}", role_id, e),
@ -126,7 +124,7 @@ impl Internal {
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. // In case the above didn't error, commit.
txn.commit(); txn.commit()?;
Ok(()) Ok(())
} }
fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> { fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> {

View File

@ -141,8 +141,10 @@ fn maybe(matches: clap::ArgMatches, log: Arc<Logger>) -> Result<(), Error> {
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(); let v = db.access.dump_roles().unwrap();
info!(log, "Roles {:?}", v); for (id, role) in v.iter() {
info!(log, "Role {}:\n{}", id, role);
}
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)?;