Impl roles

This commit is contained in:
Nadja Reitzenstein 2022-03-15 19:14:04 +01:00
parent d7467989ef
commit a111a86266
5 changed files with 119 additions and 22 deletions

View File

@ -1,7 +1,8 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use crate::authorization::permissions::PermRule; use crate::authorization::permissions::{Permission, PermRule};
use crate::users::db::UserData;
static ROLES: OnceCell<HashMap<String, Role>> = OnceCell::new(); static ROLES: OnceCell<HashMap<String, Role>> = OnceCell::new();
@ -25,6 +26,78 @@ impl Roles {
pub fn get(self, roleid: &str) -> Option<&Role> { pub fn get(self, roleid: &str) -> Option<&Role> {
self.roles.get(roleid) self.roles.get(roleid)
} }
/// Tally a role dependency tree into a set
///
/// A Default implementation exists which adapter may overwrite with more efficient
/// implementations.
fn tally_role(&self, roles: &mut HashMap<String, Role>, role_id: &String) {
if let Some(role) = self.get(role_id) {
// Only check and tally parents of a role at the role itself if it's the first time we
// see it
if !roles.contains_key(role_id) {
for parent in role.parents.iter() {
self.tally_role(roles, parent);
}
roles.insert(role_id.clone(), role.clone());
}
}
}
fn collect_permrules(&self, user: &UserData) -> Vec<PermRule> {
let mut roleset = HashMap::new();
for role_id in user.roles.iter() {
self.tally_role(&mut roleset, role_id);
}
let mut output = Vec::new();
// Iter all unique role->permissions we've found and early return on match.
for (_roleid, role) in roleset.iter() {
output.extend(role.permissions.iter().cloned())
}
output
}
fn permitted_tally(&self,
roles: &mut HashSet<String>,
role_id: &String,
perm: &Permission
) -> bool {
if let Some(role) = self.get(role_id) {
// 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_id) {
for perm_rule in role.permissions.iter() {
if perm_rule.match_perm(perm) {
return true;
}
}
for parent in role.parents.iter() {
if self.permitted_tally(roles, parent, perm) {
return true;
}
}
roles.insert(role_id.clone());
}
}
false
}
pub fn is_permitted(&self, user: &UserData, perm: impl AsRef<Permission>) -> bool {
let mut seen = HashSet::new();
for role_id in user.roles.iter() {
if self.permitted_tally(&mut seen, role_id, perm.as_ref()) {
return true;
}
}
false
}
} }
/// A "Role" from the Authorization perspective /// A "Role" from the Authorization perspective

View File

@ -10,7 +10,7 @@ use api::usersystem_capnp::user_system::{
use crate::authorization::AuthorizationHandle; use crate::authorization::AuthorizationHandle;
use crate::session::SessionHandle; use crate::session::SessionHandle;
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Users { pub struct Users {
session: SessionHandle, session: SessionHandle,
} }

View File

@ -9,6 +9,7 @@ use std::net::{SocketAddr, IpAddr, ToSocketAddrs};
use std::str::FromStr; use std::str::FromStr;
use serde::de::Error; use serde::de::Error;
use crate::authorization::permissions::{PermRule, PrivilegesBuf}; use crate::authorization::permissions::{PermRule, PrivilegesBuf};
use crate::authorization::roles::Role;
type Result<T> = std::result::Result<T, serde_dhall::Error>; type Result<T> = std::result::Result<T, serde_dhall::Error>;
@ -65,7 +66,7 @@ pub struct Config {
pub db_path: PathBuf, pub db_path: PathBuf,
pub auditlog_path: PathBuf, pub auditlog_path: PathBuf,
pub roles: HashMap<String, RoleConfig>, pub roles: HashMap<String, Role>,
#[serde(flatten)] #[serde(flatten)]
pub tlsconfig: TlsListen, pub tlsconfig: TlsListen,
@ -86,14 +87,6 @@ impl Config {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleConfig {
#[serde(default = "Vec::new")]
pub parents: Vec<String>,
#[serde(default = "Vec::new")]
pub permissions: Vec<PermRule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleConfig { pub struct ModuleConfig {
pub module: String, pub module: String,

View File

@ -3,6 +3,7 @@ use std::sync::Arc;
use futures_signals::signal::{Mutable, Signal, SignalExt}; use futures_signals::signal::{Mutable, Signal, SignalExt};
use lmdb::RoTransaction; use lmdb::RoTransaction;
use rkyv::Archived; use rkyv::Archived;
use crate::authorization::permissions::PrivilegesBuf;
use crate::config::MachineDescription; use crate::config::MachineDescription;
use crate::db::LMDBorrow; use crate::db::LMDBorrow;
use crate::resources::modules::fabaccess::{MachineState, Status}; use crate::resources::modules::fabaccess::{MachineState, Status};
@ -93,6 +94,10 @@ impl Resource {
self.inner.signal() self.inner.signal()
} }
pub fn get_required_privs(&self) -> &PrivilegesBuf {
&self.inner.desc.privs
}
fn set_state(&self, state: MachineState) { fn set_state(&self, state: MachineState) {
self.inner.set_state(state) self.inner.set_state(state)
} }

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::Context; use anyhow::Context;
use lmdb::Environment; use lmdb::Environment;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use crate::authorization::roles::Role; use crate::authorization::roles::{Role, Roles};
use crate::resources::Resource; use crate::resources::Resource;
use crate::session::db::SessionCache; use crate::session::db::SessionCache;
use crate::Users; use crate::Users;
@ -16,10 +16,13 @@ static SESSION_CACHE: OnceCell<SessionCache> = OnceCell::new();
#[derive(Clone)] #[derive(Clone)]
pub struct SessionManager { pub struct SessionManager {
users: Users, users: Users,
roles: Roles,
// cache: SessionCache // todo
} }
impl SessionManager { impl SessionManager {
pub fn new(users: Users) -> Self { pub fn new(users: Users, roles: Roles) -> Self {
Self { users } Self { users, roles }
} }
// TODO: make infallible // TODO: make infallible
@ -27,33 +30,56 @@ impl SessionManager {
let uid = uid.as_ref(); let uid = uid.as_ref();
if let Some(user) = self.users.get_user(uid) { if let Some(user) = self.users.get_user(uid) {
tracing::trace!(uid, "opening new session for user"); tracing::trace!(uid, "opening new session for user");
Some(SessionHandle { user: UserRef::new(user.id) }) Some(SessionHandle {
users: self.users.clone(),
roles: self.roles.clone(),
user: UserRef::new(user.id),
})
} else { } else {
None None
} }
} }
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct SessionHandle { pub struct SessionHandle {
users: Users,
roles: Roles,
user: UserRef, user: UserRef,
} }
impl SessionHandle { impl SessionHandle {
pub fn get_user(&self) -> UserRef { pub fn get_user(&self) -> UserRef {
unimplemented!() self.user.clone()
} }
pub fn has_disclose(&self, resource: &Resource) -> bool { pub fn has_disclose(&self, resource: &Resource) -> bool {
unimplemented!() if let Some(user) = self.users.get_user(self.user.get_username()) {
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().disclose)
} else {
false
}
} }
pub fn has_read(&self, resource: &Resource) -> bool { pub fn has_read(&self, resource: &Resource) -> bool {
unimplemented!() if let Some(user) = self.users.get_user(self.user.get_username()) {
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().read)
} else {
false
}
} }
pub fn has_write(&self, resource: &Resource) -> bool { pub fn has_write(&self, resource: &Resource) -> bool {
unimplemented!() if let Some(user) = self.users.get_user(self.user.get_username()) {
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().write)
} else {
false
}
} }
pub fn has_manage(&self, resource: &Resource) -> bool { pub fn has_manage(&self, resource: &Resource) -> bool {
unimplemented!() if let Some(user) = self.users.get_user(self.user.get_username()) {
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().manage)
} else {
false
}
} }
} }