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
/// implementations.
fn check(&self, user: &User, permID: &PermIdentifier) -> Result<bool> {
self.check_roles(&user.roles, permID)
fn check<P: AsRef<Permission>>(&self, user: &User, perm: &P) -> Result<bool> {
self.check_roles(&user.roles, perm)
}
/// 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
/// 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
let mut roleset = HashSet::new();
for roleID in roles {
@ -51,8 +51,8 @@ pub trait RoleDB {
// Iter all unique role->permissions we've found and early return on match.
for role in roleset.iter() {
for perm in role.permissions.iter() {
if permID == perm {
for perm_rule in role.permissions.iter() {
if perm_rule.match_perm(perm) {
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
/// you are allowed to manage a machine you are then also allowed to use it and so on
parents: Vec<RoleIdentifier>,
permissions: Vec<PermIdentifier>,
permissions: Vec<PermRule>,
}
type SourceID = String;
@ -186,16 +186,17 @@ fn is_sep_char(c: char) -> bool {
c == '.'
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
/// A set of privileges to a thing
pub struct PrivilegesBuf {
/// 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
read: PermissionBuf,
pub read: PermissionBuf,
/// 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
manage: PermissionBuf
pub manage: PermissionBuf
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
@ -341,11 +342,11 @@ pub enum PermRule {
impl PermRule {
// Does this rule match that permission
fn match_perm<P: AsRef<Permission>>(rule: &PermRule, perm: P) -> bool {
match rule {
PermRule::Base(base) => base.as_permission() == perm.as_ref(),
PermRule::Children(parent) => parent.as_permission() > perm.as_ref() ,
PermRule::Subtree(parent) => parent.as_permission() >= perm.as_ref(),
fn match_perm<P: AsRef<Permission>>(&self, perm: &P) -> bool {
match self {
PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
PermRule::Children(ref 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())
> 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::error::Result;
use crate::db::access::{PermIdentifier, Role, RoleIdentifier, RoleDB};
use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB};
use crate::db::user::{UserIdentifier, User};
#[derive(Clone, Debug)]
@ -34,7 +34,9 @@ impl Internal {
/// Check if a given user has the given permission
#[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
let mut roles = HashSet::new();
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.
// TODO: Change this for negative permissions?
for role in roles.iter() {
for perm in role.permissions.iter() {
if permID == perm {
for perm_rule in role.permissions.iter() {
if perm_rule.match_perm(perm) {
return Ok(true);
}
}
@ -201,9 +203,9 @@ impl 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()?;
self._check(&txn, user, permID)
self._check(&txn, user, perm)
}
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::SignalExt;
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
/// permissions.
pub struct Machine {
/// Computer-readable identifier for this machine
id: MachineIdentifier,
/// 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,
/// Descriptor of the machine
desc: MachineDescription,
/// The state of the machine as bffh thinks the machine *should* be in.
///
@ -34,11 +30,9 @@ pub struct Machine {
}
impl Machine {
pub fn new(id: Uuid, name: String, perm: access::PermIdentifier) -> Machine {
pub fn new(desc: MachineDescription, perm: access::PermIdentifier) -> Machine {
Machine {
id: id,
name: name,
perm: perm,
desc: desc,
state: Mutable::new(MachineState { state: Status::Free}),
}
}
@ -64,7 +58,8 @@ impl Machine {
, who: &User
) -> 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()) });
return Ok(true);
} else {
@ -76,3 +71,20 @@ impl Machine {
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,
}