Permission checking upgrades

This commit is contained in:
Gregor Reitzenstein 2020-11-17 13:40:44 +01:00
parent b7421528c6
commit 448cbbc98a
3 changed files with 66 additions and 34 deletions

View File

@ -33,8 +33,8 @@ 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(&self, user: &User, permID: &PermIdentifier) -> Result<bool> { fn check<P: AsRef<Permission>>(&self, user: &User, perm: &P) -> Result<bool> {
self.check_roles(&user.roles, permID) self.check_roles(&user.roles, perm)
} }
/// 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
@ -42,7 +42,7 @@ pub trait RoleDB {
/// ///
/// A Default implementation exists which adapter may overwrite with more efficient specialized /// A Default implementation exists which adapter may overwrite with more efficient specialized
/// implementations. /// implementations.
fn check_roles(&self, roles: &[RoleIdentifier], permID: &PermIdentifier) -> Result<bool> { fn check_roles<P: AsRef<Permission>>(&self, roles: &[RoleIdentifier], perm: &P) -> Result<bool> {
// Tally all roles. Makes dependent roles easier // Tally all roles. Makes dependent roles easier
let mut roleset = HashSet::new(); let mut roleset = HashSet::new();
for roleID in roles { for roleID in roles {
@ -51,8 +51,8 @@ pub trait RoleDB {
// 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.
for role in roleset.iter() { for role in roleset.iter() {
for perm in role.permissions.iter() { for perm_rule in role.permissions.iter() {
if permID == perm { if perm_rule.match_perm(perm) {
return Ok(true); return Ok(true);
} }
} }
@ -104,7 +104,7 @@ pub struct Role {
/// level of access sets the lower levels of access as parent, inheriting their permission; if /// level of access sets the lower levels of access as parent, inheriting their permission; if
/// you are allowed to manage a machine you are then also allowed to use it and so on /// you are allowed to manage a machine you are then also allowed to use it and so on
parents: Vec<RoleIdentifier>, parents: Vec<RoleIdentifier>,
permissions: Vec<PermIdentifier>, permissions: Vec<PermRule>,
} }
type SourceID = String; type SourceID = String;
@ -186,16 +186,17 @@ fn is_sep_char(c: char) -> bool {
c == '.' c == '.'
} }
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
/// A set of privileges to a thing /// A set of privileges to a thing
pub struct PrivilegesBuf { pub struct PrivilegesBuf {
/// Which permission is required to know about the existance of this thing /// Which permission is required to know about the existance of this thing
disclose: PermissionBuf, pub disclose: PermissionBuf,
/// Which permission is required to read this thing /// Which permission is required to read this thing
read: PermissionBuf, pub read: PermissionBuf,
/// Which permission is required to write parts of this thing /// Which permission is required to write parts of this thing
write: PermissionBuf, pub write: PermissionBuf,
/// Which permission is required to manage all parts of this thing /// Which permission is required to manage all parts of this thing
manage: PermissionBuf pub manage: PermissionBuf
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
@ -341,11 +342,11 @@ pub enum PermRule {
impl PermRule { 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>>(&self, perm: &P) -> bool {
match rule { match self {
PermRule::Base(base) => base.as_permission() == perm.as_ref(), PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
PermRule::Children(parent) => parent.as_permission() > perm.as_ref() , PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() ,
PermRule::Subtree(parent) => parent.as_permission() >= perm.as_ref(), PermRule::Subtree(ref parent) => parent.as_permission() >= perm.as_ref(),
} }
} }
} }
@ -372,4 +373,21 @@ mod tests {
assert!(PermissionBuf::from_string("bffh.perm".to_string()) assert!(PermissionBuf::from_string("bffh.perm".to_string())
> PermissionBuf::from_string("bffh.perm.sub".to_string())); > PermissionBuf::from_string("bffh.perm.sub".to_string()));
} }
#[test]
fn permission_simple_check_test() {
let perm = PermissionBuf::from_string("test.perm".to_string());
let rule = PermRule::Base(perm.clone());
assert!(rule.match_perm(&perm));
}
#[test]
#[should_panic]
fn permission_children_checks_only_children() {
let perm = PermissionBuf::from_string("test.perm".to_string());
let rule = PermRule::Children(perm.clone());
assert!(rule.match_perm(&perm));
}
} }

View File

@ -16,7 +16,7 @@ use lmdb::{Environment, Transaction, RwTransaction, Cursor};
use crate::config::Settings; use crate::config::Settings;
use crate::error::Result; use crate::error::Result;
use crate::db::access::{PermIdentifier, Role, RoleIdentifier, RoleDB}; use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB};
use crate::db::user::{UserIdentifier, User}; use crate::db::user::{UserIdentifier, User};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -34,7 +34,9 @@ impl Internal {
/// 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, user: &User, permID: &PermIdentifier) -> Result<bool> { pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &User, perm: &P)
-> Result<bool>
{
// 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.iter() { for roleID in user.roles.iter() {
@ -44,8 +46,8 @@ impl Internal {
// 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_rule in role.permissions.iter() {
if permID == perm { if perm_rule.match_perm(perm) {
return Ok(true); return Ok(true);
} }
} }
@ -201,9 +203,9 @@ impl Internal {
} }
impl RoleDB for Internal { impl RoleDB for Internal {
fn check(&self, user: &User, permID: &PermIdentifier) -> Result<bool> { fn check<P: AsRef<Permission>>(&self, user: &User, perm: &P) -> Result<bool> {
let txn = self.env.begin_ro_txn()?; let txn = self.env.begin_ro_txn()?;
self._check(&txn, user, permID) self._check(&txn, user, perm)
} }
fn get_role(&self, roleID: &RoleIdentifier) -> Result<Option<Role>> { fn get_role(&self, roleID: &RoleIdentifier) -> Result<Option<Role>> {

View File

@ -1,3 +1,5 @@
use serde::{Serialize, Deserialize};
use futures_signals::signal::Signal; use futures_signals::signal::Signal;
use futures_signals::signal::SignalExt; use futures_signals::signal::SignalExt;
use futures_signals::signal::Mutable; use futures_signals::signal::Mutable;
@ -17,14 +19,8 @@ use crate::db::machine::{MachineIdentifier, Status, MachineState};
/// machine, checking that the user who wants the machine (de)activated has the required /// machine, checking that the user who wants the machine (de)activated has the required
/// permissions. /// permissions.
pub struct Machine { pub struct Machine {
/// Computer-readable identifier for this machine /// Descriptor of the machine
id: MachineIdentifier, desc: MachineDescription,
/// The human-readable name of the machine. Does not need to be unique
name: String,
/// The required permissions to use this machine.
perm: access::PermIdentifier,
/// The state of the machine as bffh thinks the machine *should* be in. /// The state of the machine as bffh thinks the machine *should* be in.
/// ///
@ -34,11 +30,9 @@ pub struct Machine {
} }
impl Machine { impl Machine {
pub fn new(id: Uuid, name: String, perm: access::PermIdentifier) -> Machine { pub fn new(desc: MachineDescription, perm: access::PermIdentifier) -> Machine {
Machine { Machine {
id: id, desc: desc,
name: name,
perm: perm,
state: Mutable::new(MachineState { state: Status::Free}), state: Mutable::new(MachineState { state: Status::Free}),
} }
} }
@ -64,7 +58,8 @@ impl Machine {
, who: &User , who: &User
) -> Result<bool> ) -> Result<bool>
{ {
if pp.check(who, &self.perm)? { // TODO: Check different levels
if pp.check(who, &self.desc.privs.write)? {
self.state.set(MachineState { state: Status::InUse(who.id.clone()) }); self.state.set(MachineState { state: Status::InUse(who.id.clone()) });
return Ok(true); return Ok(true);
} else { } else {
@ -76,3 +71,20 @@ impl Machine {
self.state.set(MachineState { state }) self.state.set(MachineState { state })
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
/// A description of a machine
///
/// This is the struct that a machine is serialized to/from.
/// Combining this with the actual state of the system will return a machine
pub struct MachineDescription {
/// The main machine identifier. This must be unique.
id: MachineIdentifier,
/// The name of the machine. Doesn't need to be unique but is what humans will be presented.
name: String,
/// An optional description of the Machine.
description: Option<String>,
/// The permission required
privs: access::PrivilegesBuf,
}