mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-26 08:34:55 +01:00
Implements a simple permission check
This commit is contained in:
parent
7f6e4c0759
commit
565a5ed278
@ -1,31 +1,107 @@
|
|||||||
//! Access control logic
|
//! Access control logic
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use flexbuffers;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
|
use lmdb::{Transaction, RoTransaction, RwTransaction};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::error::Result;
|
||||||
|
|
||||||
|
type UserIdentifier = u64;
|
||||||
|
type RoleIdentifier = u64;
|
||||||
|
type PermIdentifier = u64;
|
||||||
|
|
||||||
pub struct PermissionsProvider {
|
pub struct PermissionsProvider {
|
||||||
log: Logger,
|
log: Logger,
|
||||||
|
roledb: lmdb::Database,
|
||||||
|
permdb: lmdb::Database,
|
||||||
|
userdb: lmdb::Database,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionsProvider {
|
impl PermissionsProvider {
|
||||||
pub fn new(log: Logger) -> Self {
|
pub fn new(log: Logger, roledb: lmdb::Database, permdb: lmdb::Database, userdb: lmdb::Database) -> Self {
|
||||||
Self { log }
|
Self { log, roledb, permdb, userdb }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a given user has the given permission
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn check<T: Transaction>(&self, txn: &T, userID: UserIdentifier, permID: PermIdentifier) -> Result<bool> {
|
||||||
|
if let Some(user) = self.get_user(txn, userID)? {
|
||||||
|
// Tally all roles. Makes dependent roles easier
|
||||||
|
let mut roles = HashSet::new();
|
||||||
|
for roleID in user.roles {
|
||||||
|
self.tally_role(txn, &mut roles, roleID)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tally_role<T: Transaction>(&self, txn: &T, roles: &mut HashSet<Role>, roleID: RoleIdentifier) -> Result<()> {
|
||||||
|
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
|
||||||
|
// see it
|
||||||
|
if !roles.contains(&role) {
|
||||||
|
for parent in role.parents.iter() {
|
||||||
|
self.tally_role(txn, roles, *parent)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
roles.insert(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_role<'txn, T: Transaction>(&self, txn: &'txn T, roleID: RoleIdentifier) -> Result<Option<Role>> {
|
||||||
|
match txn.get(self.roledb, &roleID.to_ne_bytes()) {
|
||||||
|
Ok(bytes) => {
|
||||||
|
Ok(Some(flexbuffers::from_slice(bytes)?))
|
||||||
|
},
|
||||||
|
Err(lmdb::Error::NotFound) => { Ok(None) },
|
||||||
|
Err(e) => { Err(e.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_user<T: Transaction>(&self, txn: &T, userID: UserIdentifier) -> Result<Option<User>> {
|
||||||
|
match txn.get(self.userdb, &userID.to_ne_bytes()) {
|
||||||
|
Ok(bytes) => {
|
||||||
|
Ok(Some(flexbuffers::from_slice(bytes)?))
|
||||||
|
},
|
||||||
|
Err(lmdb::Error::NotFound) => { Ok(None) },
|
||||||
|
Err(e) => { Err(e.into()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This line documents init
|
/// This line documents init
|
||||||
pub fn init(log: Logger, config: &Config, env: &lmdb::Environment) -> std::result::Result<PermissionsProvider, crate::error::Error> {
|
pub fn init(log: Logger, config: &Config, env: &lmdb::Environment) -> std::result::Result<PermissionsProvider, crate::error::Error> {
|
||||||
return Ok(PermissionsProvider::new(log));
|
let mut flags = lmdb::DatabaseFlags::empty();
|
||||||
|
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
|
||||||
|
let roledb = env.create_db(Some("role"), flags)?;
|
||||||
|
let permdb = env.create_db(Some("perm"), flags)?;
|
||||||
|
let userdb = env.create_db(Some("user"), flags)?;
|
||||||
|
return Ok(PermissionsProvider::new(log, roledb, permdb, userdb));
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoleIdentifier = u64;
|
|
||||||
type PermIdentifier = u64;
|
|
||||||
|
|
||||||
/// A Person, from the Authorization perspective
|
/// A Person, from the Authorization perspective
|
||||||
struct Person {
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
struct User {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
/// A Person has N ≥ 0 roles.
|
/// A Person has N ≥ 0 roles.
|
||||||
@ -45,6 +121,7 @@ struct Person {
|
|||||||
/// of a machine; if later on a similar enough machine is put to use the administrator can just add
|
/// of a machine; if later on a similar enough machine is put to use the administrator can just add
|
||||||
/// the permission for that machine to an already existing role instead of manually having to
|
/// the permission for that machine to an already existing role instead of manually having to
|
||||||
/// assign to all users.
|
/// assign to all users.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
struct Role {
|
struct Role {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
@ -61,6 +138,7 @@ struct Role {
|
|||||||
///
|
///
|
||||||
/// Permissions are rather simple flags. A person can have or not have a permission, dictated by
|
/// Permissions are rather simple flags. A person can have or not have a permission, dictated by
|
||||||
/// its roles and the permissions assigned to those roles.
|
/// its roles and the permissions assigned to those roles.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
struct Permission {
|
struct Permission {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
20
src/error.rs
20
src/error.rs
@ -13,6 +13,8 @@ pub enum Error {
|
|||||||
Boxed(Box<dyn std::error::Error>),
|
Boxed(Box<dyn std::error::Error>),
|
||||||
Capnp(capnp::Error),
|
Capnp(capnp::Error),
|
||||||
LMDB(lmdb::Error),
|
LMDB(lmdb::Error),
|
||||||
|
FlexbuffersDe(flexbuffers::DeserializationError),
|
||||||
|
FlexbuffersSer(flexbuffers::SerializationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@ -39,6 +41,12 @@ impl fmt::Display for Error {
|
|||||||
Error::LMDB(e) => {
|
Error::LMDB(e) => {
|
||||||
write!(f, "LMDB Error: {}", e)
|
write!(f, "LMDB Error: {}", e)
|
||||||
},
|
},
|
||||||
|
Error::FlexbuffersDe(e) => {
|
||||||
|
write!(f, "Flexbuffers decoding error: {}", e)
|
||||||
|
},
|
||||||
|
Error::FlexbuffersSer(e) => {
|
||||||
|
write!(f, "Flexbuffers encoding error: {}", e)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,4 +93,16 @@ impl From<lmdb::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<flexbuffers::DeserializationError> for Error {
|
||||||
|
fn from(e: flexbuffers::DeserializationError) -> Error {
|
||||||
|
Error::FlexbuffersDe(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<flexbuffers::SerializationError> for Error {
|
||||||
|
fn from(e: flexbuffers::SerializationError) -> Error {
|
||||||
|
Error::FlexbuffersSer(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
Loading…
Reference in New Issue
Block a user