Make compile

This commit is contained in:
Gregor Reitzenstein 2020-10-28 23:24:02 +01:00
parent d304ff40d4
commit 2027d9696e
6 changed files with 143 additions and 80 deletions

View File

@ -3,6 +3,7 @@
use std::fmt; use std::fmt;
use std::collections::HashSet; use std::collections::HashSet;
use std::cmp::Ordering;
use std::convert::TryInto; use std::convert::TryInto;
@ -22,18 +23,19 @@ use crate::error::Result;
mod adapter_lmdb; mod adapter_lmdb;
use crate::db::user::User;
use adapter_lmdb::PermissionsDB; use adapter_lmdb::PermissionsDB;
pub use adapter_lmdb::init; pub use adapter_lmdb::init;
pub trait RoleDB { pub trait RoleDB {
fn get_role(&self, roleID: RoleIdentifier) -> Result<Option<Role>>; fn get_role(&self, roleID: &RoleIdentifier) -> Result<Option<Role>>;
/// Check if a given user has the given permission /// Check if a given user has the given permission
/// ///
/// Default implementation which adapter may overwrite with more efficient specialized /// Default implementation which adapter may overwrite with more efficient specialized
/// implementations. /// implementations.
fn check(&self, user: &User, permID: PermIdentifier) -> Result<bool> { fn check(&self, user: &User, permID: &PermIdentifier) -> Result<bool> {
self.check_roles(user.roles) self.check_roles(&user.roles, permID)
} }
/// Check if a given permission is granted by any of the given roles or their respective /// Check if a given permission is granted by any of the given roles or their respective
@ -41,18 +43,17 @@ pub trait RoleDB {
/// ///
/// Default implementation which adapter may overwrite with more efficient specialized /// Default implementation which adapter may overwrite with more efficient specialized
/// implementations. /// implementations.
fn check_roles(&self, roles: &[RoleIdentifier], permID: PermIdentifier) -> Result<bool> { fn check_roles(&self, roles: &[RoleIdentifier], permID: &PermIdentifier) -> Result<bool> {
// Tally all roles. Makes dependent roles easier // Tally all roles. Makes dependent roles easier
let mut roles = HashSet::new(); let mut roleset = HashSet::new();
for roleID in roles { for roleID in roles {
self.tally_role(txn, &mut roles, 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.
// TODO: Change this for negative permissions? for role in roleset.iter() {
for role in roles.iter() {
for perm in role.permissions.iter() { for perm in role.permissions.iter() {
if permID == *perm { if permID == perm {
return Ok(true); return Ok(true);
} }
} }
@ -64,13 +65,13 @@ pub trait RoleDB {
/// Tally a role dependency tree into a set /// Tally a role dependency tree into a set
/// ///
/// Default implementation which adapter may overwrite with more efficient implementations /// Default implementation which adapter may overwrite with more efficient implementations
fn tally_role(&self, roles: &mut HashSet<Role>, roleID: RoleIdentifier) -> Result<()> { fn tally_role(&self, roles: &mut HashSet<Role>, roleID: &RoleIdentifier) -> Result<()> {
if let Some(role) = self.get_role(txn, 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(&role) {
for parent in role.parents.iter() { for parent in role.parents.iter() {
self.tally_role(txn, roles, *parent)?; self.tally_role(roles, parent)?;
} }
roles.insert(role); roles.insert(role);
@ -108,8 +109,15 @@ pub struct Role {
type SourceID = String; type SourceID = String;
fn split_once(s: &str, split: char) -> Option<(&str, &str)> {
s
.find(split)
.map(|idx| s.split_at(idx))
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
/// Universal (relative) id of a role /// Universal (relative) id of a role
enum RoleIdentifier { pub enum RoleIdentifier {
/// The role comes from this instance /// The role comes from this instance
Local { Local {
/// Locally unique name for the role. No other role at this instance no matter the source /// Locally unique name for the role. No other role at this instance no matter the source
@ -127,16 +135,35 @@ enum RoleIdentifier {
location: String, location: String,
} }
} }
impl fmt::Display for RoleID { impl std::str::FromStr for RoleIdentifier {
type Err = RoleFromStrError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Some((name, location)) = split_once(s, '@') {
Ok(RoleIdentifier::Remote { name: name.to_string(), location: location.to_string() })
} else if let Some((name, source)) = split_once(s, '%') {
Ok(RoleIdentifier::Local { name: name.to_string(), source: source.to_string() })
} else {
Err(RoleFromStrError::Invalid)
}
}
}
impl fmt::Display for RoleIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match self {
RoleIdentifier::Local {name, source} => write!(f, "{}/{}@local", name, source), RoleIdentifier::Local {name, source} => write!(f, "{}/{}@local", name, source),
RoleIdentifier::Remote {name, location} => write!(f, "{}@{}", name, location), RoleIdentifier::Remote {name, location} => write!(f, "{}@{}", name, location),
} }
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RoleFromStrError {
/// No '@' or '%' found. That's strange, huh?
Invalid
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
/// An identifier for a permission /// An identifier for a permission
// XXX: Does remote permissions ever make sense? // XXX: Does remote permissions ever make sense?
// I mean we kinda get them for free so maybe? // I mean we kinda get them for free so maybe?
@ -146,7 +173,7 @@ pub enum PermIdentifier {
} }
impl fmt::Display for PermIdentifier { impl fmt::Display for PermIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match self {
PermIdentifier::Local(perm) PermIdentifier::Local(perm)
=> write!(f, "{}", perm), => write!(f, "{}", perm),
PermIdentifier::Remote(perm, source) PermIdentifier::Remote(perm, source)
@ -155,7 +182,11 @@ impl fmt::Display for PermIdentifier {
} }
} }
#[derive(Clone, Serialize, Deserialize)] fn is_sep_char(c: char) -> bool {
c == '.'
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(transparent)] #[repr(transparent)]
/// An owned permission string /// An owned permission string
/// ///
@ -173,13 +204,13 @@ impl PermissionBuf {
} }
/// Allocate a `PermissionBuf` with the given capacity given to the internal [`String`] /// Allocate a `PermissionBuf` with the given capacity given to the internal [`String`]
pub fn with_capacity() -> Self { pub fn with_capacity(cap: usize) -> Self {
PermissionBuf { inner: String::with_capacity() } PermissionBuf { inner: String::with_capacity(cap) }
} }
#[inline(always)] #[inline(always)]
pub fn as_permission(&self) -> &Permission { pub fn as_permission(&self) -> &Permission {
self self.as_ref()
} }
pub fn push<P: AsRef<Permission>>(&mut self, perm: P) { pub fn push<P: AsRef<Permission>>(&mut self, perm: P) {
@ -192,22 +223,39 @@ impl PermissionBuf {
if need_sep { if need_sep {
self.inner.push('.') self.inner.push('.')
} }
self.inner.push(perm.as_str()) self.inner.push_str(perm.as_str())
} }
pub fn from_string(inner: String) -> Self { pub fn from_string(inner: String) -> Self {
Self { inner } Self { inner }
} }
} }
impl AsRef<Permission> for PermissionBuf { impl AsRef<str> for PermissionBuf {
#[inline(always)] #[inline(always)]
fn as_ref(&self) -> &str {
&self.inner[..]
}
}
impl AsRef<Permission> for PermissionBuf {
#[inline]
fn as_ref(&self) -> &Permission { fn as_ref(&self) -> &Permission {
self.as_permission() Permission::new(self)
}
}
impl PartialOrd for PermissionBuf {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let a: &Permission = self.as_ref();
a.partial_cmp(other.as_ref())
}
}
impl fmt::Display for PermissionBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
} }
} }
#[repr(transparent)] #[repr(transparent)]
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Hash)]
/// A borrowed permission string /// A borrowed permission string
/// ///
/// Permissions have total equality and partial ordering. /// Permissions have total equality and partial ordering.
@ -218,21 +266,27 @@ pub struct Permission {
inner: str inner: str
} }
impl Permission { impl Permission {
pub fn as_str(&self) -> &str { pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
self.inner unsafe { &*(s.as_ref() as *const str as *const Permission) }
} }
pub fn iter(&self) -> std::str::Split<Char> { pub fn as_str(&self) -> &str {
&self.inner
}
pub fn iter(&self) -> std::str::Split<char> {
self.inner.split('.') self.inner.split('.')
} }
} }
impl PartialOrd for Permission { impl PartialOrd for Permission {
fn partial_cmp(&self, other: &Permission) -> Option<Ordering> { fn partial_cmp(&self, other: &Permission) -> Option<Ordering> {
let (l,r) = (None, None); let mut i = self.iter();
let mut j = other.iter();
let (mut l, mut r) = (None, None);
while { while {
l = self.next(); l = i.next();
r = other.next(); r = j.next();
l.is_some() && r.is_some() l.is_some() && r.is_some()
} { } {
@ -243,7 +297,7 @@ impl PartialOrd for Permission {
match (l,r) { match (l,r) {
(None, None) => Some(Ordering::Equal), (None, None) => Some(Ordering::Equal),
(Some(_), None) => Some(Ordering::Lesser), (Some(_), None) => Some(Ordering::Less),
(None, Some(_)) => Some(Ordering::Greater), (None, Some(_)) => Some(Ordering::Greater),
(Some(_), Some(_)) => panic!("Broken contract in Permission::partial_cmp: sides should never be both Some!"), (Some(_), Some(_)) => panic!("Broken contract in Permission::partial_cmp: sides should never be both Some!"),
} }
@ -251,7 +305,7 @@ impl PartialOrd for Permission {
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PermRule { pub enum PermRule {
/// The permission is precise, /// The permission is precise,
/// ///
@ -277,16 +331,16 @@ impl PermRule {
// Does this rule match that permission // Does this rule match that permission
fn match_perm<P: AsRef<Permission>>(rule: &PermRule, perm: P) -> bool { fn match_perm<P: AsRef<Permission>>(rule: &PermRule, perm: P) -> bool {
match rule { match rule {
Base(base) => base == perm, PermRule::Base(base) => base.as_permission() == perm.as_ref(),
Children(parent) => parent > perm , PermRule::Children(parent) => parent.as_permission() > perm.as_ref() ,
Subtree(parent) => parent >= perm, PermRule::Subtree(parent) => parent.as_permission() >= perm.as_ref(),
} }
} }
} }
impl fmt::Display for PermRule { impl fmt::Display for PermRule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match self {
PermRule::Base(perm) PermRule::Base(perm)
=> write!(f, "{}", perm), => write!(f, "{}", perm),
PermRule::Children(parent) PermRule::Children(parent)

View File

@ -34,35 +34,33 @@ impl PermissionsDB {
/// Check if a given user has the given permission /// Check if a given user has the given permission
#[allow(unused)] #[allow(unused)]
pub fn _check<T: Transaction>(&self, txn: &T, userID: UserIdentifier, permID: PermIdentifier) -> Result<bool> { pub fn _check<T: Transaction>(&self, txn: &T, user: &User, permID: &PermIdentifier) -> Result<bool> {
if let Some(user) = self.get_user(txn, userID)? {
// Tally all roles. Makes dependent roles easier // Tally all roles. Makes dependent roles easier
let mut roles = HashSet::new(); let mut roles = HashSet::new();
for roleID in user.roles { for roleID in user.roles.iter() {
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 role in roles.iter() {
for perm in role.permissions.iter() { for perm in role.permissions.iter() {
if permID == *perm { if permID == perm {
return Ok(true); return Ok(true);
} }
} }
} }
}
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 HashSet<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(&role) {
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(role);
@ -72,8 +70,9 @@ impl PermissionsDB {
Ok(()) Ok(())
} }
pub fn _get_role<'txn, T: Transaction>(&self, txn: &'txn T, roleID: RoleIdentifier) -> Result<Option<Role>> { pub fn _get_role<'txn, T: Transaction>(&self, txn: &'txn T, roleID: &RoleIdentifier) -> Result<Option<Role>> {
match txn.get(self.roledb, &roleID.to_ne_bytes()) { let string = format!("{}", roleID);
match txn.get(self.roledb, &string.as_bytes()) {
Ok(bytes) => { Ok(bytes) => {
Ok(Some(flexbuffers::from_slice(bytes)?)) Ok(Some(flexbuffers::from_slice(bytes)?))
}, },
@ -82,9 +81,10 @@ impl PermissionsDB {
} }
} }
fn put_role(&self, txn: &mut RwTransaction, roleID: RoleIdentifier, role: Role) -> Result<()> { fn put_role(&self, txn: &mut RwTransaction, roleID: &RoleIdentifier, role: Role) -> Result<()> {
let bytes = flexbuffers::to_vec(role)?; let bytes = flexbuffers::to_vec(role)?;
txn.put(self.roledb, &roleID.to_ne_bytes(), &bytes, lmdb::WriteFlags::empty())?; let string = format!("{}", roleID);
txn.put(self.roledb, &string.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
Ok(()) Ok(())
} }
@ -164,10 +164,10 @@ impl PermissionsDB {
let roleID_str = path let roleID_str = path
.file_stem().expect("Found a file with no filename?") .file_stem().expect("Found a file with no filename?")
.to_str().expect("Found an OsStr that isn't valid Unicode. Fix your OS!"); .to_str().expect("Found an OsStr that isn't valid Unicode. Fix your OS!");
let roleID = match u64::from_str_radix(roleID_str, 16) { let roleID = match str::parse(roleID_str) {
Ok(i) => i, Ok(i) => i,
Err(e) => { 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); warn!(self.log, "File {} had a invalid name.", path.display());
continue; continue;
} }
}; };
@ -189,8 +189,8 @@ impl PermissionsDB {
continue; continue;
} }
}; };
self.put_role(txn, roleID, role)?; self.put_role(txn, &roleID, role)?;
debug!(self.log, "Loaded role {}", roleID); debug!(self.log, "Loaded role {}", &roleID);
} else { } else {
warn!(self.log, "Path {} is not a file, skipping!", path.display()); warn!(self.log, "Path {} is not a file, skipping!", path.display());
} }
@ -201,21 +201,28 @@ impl PermissionsDB {
} }
impl RoleDB for PermissionsDB { impl RoleDB for PermissionsDB {
fn check(&self, userID: UserIdentifier, permID: PermIdentifier) -> Result<bool> { fn check(&self, user: &User, permID: &PermIdentifier) -> Result<bool> {
let txn = self.env.begin_ro_txn()?; let txn = self.env.begin_ro_txn()?;
self._check(&txn, userID, permID) self._check(&txn, user, permID)
} }
fn get_role(&self, roleID: RoleIdentifier) -> Result<Option<Role>> { fn get_role(&self, roleID: &RoleIdentifier) -> Result<Option<Role>> {
let txn = self.env.begin_ro_txn()?; let txn = self.env.begin_ro_txn()?;
self._get_role(&txn, roleID) self._get_role(&txn, roleID)
} }
fn tally_role(&self, roles: &mut HashSet<Role>, roleID: &RoleIdentifier) -> Result<()> {
let txn = self.env.begin_ro_txn()?;
self._tally_role(&txn, roles, roleID)
}
} }
/// Initialize the access db by loading all the lmdb databases /// Initialize the access db by loading all the lmdb databases
pub fn init(log: Logger, config: &Settings, env: Arc<lmdb::Environment>) -> std::result::Result<Permissions, crate::error::Error> { pub fn init(log: Logger, config: &Settings, env: Arc<lmdb::Environment>)
-> std::result::Result<PermissionsDB, crate::error::Error>
{
let mut flags = lmdb::DatabaseFlags::empty(); let mut flags = lmdb::DatabaseFlags::empty();
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
let roledb = env.create_db(Some("role"), flags)?; let roledb = env.create_db(Some("role"), flags)?;

View File

@ -16,6 +16,8 @@ use crate::error::Result;
use crate::config::Settings; use crate::config::Settings;
use crate::access; use crate::access;
use crate::db::user::UserIdentifier;
use capnp::Error; use capnp::Error;
use uuid::Uuid; use uuid::Uuid;
@ -27,6 +29,7 @@ use smol::channel::{Receiver, Sender};
use futures_signals::signal::*; use futures_signals::signal::*;
use crate::registries::StatusSignal; use crate::registries::StatusSignal;
use crate::db::user::User;
pub type ID = Uuid; pub type ID = Uuid;
@ -139,14 +142,13 @@ impl Machine {
/// Requests to use a machine. Returns `true` if successful. /// Requests to use a machine. Returns `true` if successful.
/// ///
/// This will update the internal state of the machine, notifying connected actors, if any. /// This will update the internal state of the machine, notifying connected actors, if any.
pub fn request_use<T: Transaction> pub fn request_use<P: access::RoleDB>
( &mut self ( &mut self
, txn: &T , pp: &P
, pp: &access::PermissionsDB , who: &User
, who: access::UserIdentifier
) -> Result<bool> ) -> Result<bool>
{ {
if pp.check(txn, who, self.perm)? { if pp.check(who, &self.perm)? {
self.state.set(Status::Occupied); self.state.set(Status::Occupied);
return Ok(true); return Ok(true);
} else { } else {

View File

@ -20,7 +20,7 @@ pub struct User {
/// Locally unique identifier for an user /// Locally unique identifier for an user
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct UserIdentifier { pub struct UserIdentifier {
/// Main UID. Must be unique in this instance so that the tuple (uid, location) is globally /// Main UID. Must be unique in this instance so that the tuple (uid, location) is globally
/// unique. /// unique.
@ -42,10 +42,10 @@ impl UserIdentifier {
impl fmt::Display for UserIdentifier { impl fmt::Display for UserIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let r = write!(f, "{}", self.uid); let r = write!(f, "{}", self.uid);
if let Some(s) = self.subuid { if let Some(ref s) = self.subuid {
write!(f, "+{}", s)?; write!(f, "+{}", s)?;
} }
if let Some(l) = self.location { if let Some(ref l) = self.location {
write!(f, "@{}", l)?; write!(f, "@{}", l)?;
} }
r r
@ -70,10 +70,10 @@ mod tests {
assert_eq!("testuser", assert_eq!("testuser",
format!("{}", UserIdentifier::new(uid, None, None))); format!("{}", UserIdentifier::new(uid, None, None)));
assert_eq!("testuser+testsuid", assert_eq!("testuser+testsuid",
format!(UserIdentifier::new("testuser", Some(suid), None))); format!("{}", UserIdentifier::new(uid, Some(suid), None)));
assert_eq!("testuser+testsuid", assert_eq!("testuser+testsuid",
format!(UserIdentifier::new("testuser", Some(suid), None))); format!("{}", UserIdentifier::new(uid, Some(suid), None)));
assert_eq!("testuser+testsuid@testloc", assert_eq!("testuser+testsuid@testloc",
format!(UserIdentifier::new("testuser", Some(suid), Some(location)))); format!("{}", UserIdentifier::new(uid, Some(suid), Some(location))));
} }
} }

View File

@ -163,7 +163,7 @@ fn main() -> Result<(), Error> {
let mut txn = env.begin_rw_txn()?; let mut txn = env.begin_rw_txn()?;
let path = path.to_path_buf(); let path = path.to_path_buf();
pdb?.inner.load_db(&mut txn, path.clone())?; pdb?.load_db(&mut txn, path.clone())?;
mdb?.load_db(&mut txn, path)?; mdb?.load_db(&mut txn, path)?;
txn.commit(); txn.commit();
} else { } else {
@ -181,7 +181,7 @@ fn main() -> Result<(), Error> {
let txn = env.begin_ro_txn()?; let txn = env.begin_ro_txn()?;
let path = path.to_path_buf(); let path = path.to_path_buf();
pdb?.inner.dump_db(&txn, path.clone())?; pdb?.dump_db(&txn, path.clone())?;
mdb?.dump_db(&txn, path)?; mdb?.dump_db(&txn, path)?;
} else { } else {
error!(log, "You must provide a directory path to dump into"); error!(log, "You must provide a directory path to dump into");

View File

@ -35,5 +35,5 @@ impl Network {
enum Event { enum Event {
/// An user wants to use a machine /// An user wants to use a machine
// TODO: Define /what/ an user wants to do with said machine? // TODO: Define /what/ an user wants to do with said machine?
MachineRequest(machine::ID, access::UserIdentifier), MachineRequest(machine::ID, UserIdentifier),
} }