Session initialization

This commit is contained in:
Nadja Reitzenstein 2022-03-15 17:52:47 +01:00
parent 2e9c7fbc19
commit 5c9b72c37d
12 changed files with 179 additions and 156 deletions

View File

@ -1,11 +1,12 @@
use std::sync::Arc;
use rsasl::error::{SASLError, SessionError};
use rsasl::mechname::Mechname;
use rsasl::{Property, SASL};
use rsasl::session::{Session, SessionData};
use rsasl::validate::Validation;
use crate::users::db::UserDB; use crate::users::db::UserDB;
use crate::users::Users; use crate::users::Users;
use rsasl::error::{SASLError, SessionError};
use rsasl::mechname::Mechname;
use rsasl::property::{AuthId, Password};
use rsasl::session::{Session, SessionData};
use rsasl::validate::{validations, Validation};
use rsasl::{Property, SASL};
use std::sync::Arc;
pub mod db; pub mod db;
@ -14,12 +15,40 @@ struct Callback {
} }
impl Callback { impl Callback {
pub fn new(users: Users) -> Self { pub fn new(users: Users) -> Self {
Self { users, } Self { users }
} }
} }
impl rsasl::callback::Callback for Callback { impl rsasl::callback::Callback for Callback {
fn validate(&self, session: &mut SessionData, validation: Validation, mechanism: &Mechname) -> Result<(), SessionError> { fn validate(
todo!() &self,
session: &mut SessionData,
validation: Validation,
mechanism: &Mechname,
) -> Result<(), SessionError> {
match validation {
validations::SIMPLE => {
let authnid = session
.get_property::<AuthId>()
.ok_or(SessionError::no_property::<AuthId>())?;
let user = self
.users
.get_user(authnid.as_str())
.ok_or(SessionError::AuthenticationFailure)?;
let passwd = session
.get_property::<Password>()
.ok_or(SessionError::no_property::<Password>())?;
if user
.check_password(passwd.as_bytes())
.map_err(|e| SessionError::AuthenticationFailure)?
{
Ok(())
} else {
Err(SessionError::AuthenticationFailure)
}
}
_ => Err(SessionError::no_validate(validation)),
}
} }
} }
@ -41,7 +70,9 @@ impl AuthenticationHandle {
pub fn new(userdb: Users) -> Self { pub fn new(userdb: Users) -> Self {
let mut rsasl = SASL::new(); let mut rsasl = SASL::new();
rsasl.install_callback(Arc::new(Callback::new(userdb))); rsasl.install_callback(Arc::new(Callback::new(userdb)));
Self { inner: Arc::new(Inner::new(rsasl)) } Self {
inner: Arc::new(Inner::new(rsasl)),
}
} }
pub fn start(&self, mechanism: &Mechname) -> anyhow::Result<Session> { pub fn start(&self, mechanism: &Mechname) -> anyhow::Result<Session> {
@ -49,6 +80,10 @@ impl AuthenticationHandle {
} }
pub fn list_available_mechs(&self) -> impl IntoIterator<Item = &Mechname> { pub fn list_available_mechs(&self) -> impl IntoIterator<Item = &Mechname> {
self.inner.rsasl.server_mech_list().into_iter().map(|m| m.mechanism) self.inner
.rsasl
.server_mech_list()
.into_iter()
.map(|m| m.mechanism)
} }
} }

View File

@ -1,34 +1,26 @@
use std::sync::Arc; use std::sync::Arc;
use crate::authorization::permissions::Permission; use crate::authorization::permissions::Permission;
use crate::authorization::roles::Role; use crate::authorization::roles::{Role, Roles};
use crate::Users; use crate::Users;
use crate::users::User; use crate::users::UserRef;
pub mod permissions; pub mod permissions;
pub mod roles; pub mod roles;
struct Inner {
users: Users,
}
impl Inner {
pub fn new(users: Users) -> Self {
Self { users }
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct AuthorizationHandle { pub struct AuthorizationHandle {
users: Users, users: Users,
roles: Roles,
} }
impl AuthorizationHandle { impl AuthorizationHandle {
pub fn new(users: Users) -> Self { pub fn new(users: Users, roles: Roles) -> Self {
Self { users } Self { users, roles }
} }
pub fn get_user_roles(&self, uid: impl AsRef<str>) -> Option<impl IntoIterator<Item=Role>> { pub fn get_user_roles(&self, uid: impl AsRef<str>) -> Option<Vec<String>> {
let user = self.users.get_user(uid.as_ref())?; let user = self.users.get_user(uid.as_ref())?;
Some([]) Some(user.userdata.roles.clone())
} }
pub fn is_permitted<'a>(&self, roles: impl IntoIterator<Item=&'a Role>, perm: impl AsRef<Permission>) -> bool { pub fn is_permitted<'a>(&self, roles: impl IntoIterator<Item=&'a Role>, perm: impl AsRef<Permission>) -> bool {

View File

@ -1,6 +1,32 @@
use std::collections::HashMap;
use std::fmt; use std::fmt;
use once_cell::sync::OnceCell;
use crate::authorization::permissions::PermRule; use crate::authorization::permissions::PermRule;
static ROLES: OnceCell<HashMap<String, Role>> = OnceCell::new();
#[derive(Copy, Clone)]
pub struct Roles {
roles: &'static HashMap<String, Role>,
}
impl Roles {
pub fn new(roles: HashMap<String, Role>) -> Self {
let span = tracing::debug_span!("roles", "Creating Roles handle");
let _guard = span.enter();
let this = ROLES.get_or_init(|| {
tracing::debug!("Initializing global roles…");
roles
});
Self { roles: this }
}
pub fn get(self, roleid: &str) -> Option<&Role> {
self.roles.get(roleid)
}
}
/// A "Role" from the Authorization perspective /// A "Role" from the Authorization perspective
/// ///
/// You can think of a role as a bundle of permissions relating to other roles. In most cases a /// You can think of a role as a bundle of permissions relating to other roles. In most cases a
@ -22,7 +48,7 @@ pub struct Role {
/// This makes situations where different levels of access are required easier: Each higher /// This makes situations where different levels of access are required easier: Each higher
/// 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<String>,
// If a role doesn't define permissions, default to an empty Vec. // If a role doesn't define permissions, default to an empty Vec.
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
@ -30,7 +56,7 @@ pub struct Role {
} }
impl Role { impl Role {
pub fn new(parents: Vec<RoleIdentifier>, permissions: Vec<PermRule>) -> Self { pub fn new(parents: Vec<String>, permissions: Vec<PermRule>) -> Self {
Self { parents, permissions } Self { parents, permissions }
} }
} }
@ -59,88 +85,3 @@ impl fmt::Display for Role {
Ok(()) Ok(())
} }
} }
type SourceID = String;
fn split_once(s: &str, split: char) -> Option<(&str, &str)> {
s
.find(split)
.map(|idx| (&s[..idx], &s[(idx+1)..]))
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(try_from = "String")]
#[serde(into = "String")]
/// Universal (relative) id of a role
pub struct RoleIdentifier {
/// Locally unique name for the role. No other role at this instance no matter the source
/// may have the same name
name: String,
/// Role Source, i.e. the database the role comes from
source: SourceID,
}
impl RoleIdentifier {
pub fn new<>(name: &str, source: &str) -> Self {
Self { name: name.to_string(), source: source.to_string() }
}
pub fn from_strings(name: String, source: String) -> Self {
Self { name, source }
}
}
impl fmt::Display for RoleIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.source != "" {
write!(f, "{}/{}", self.name, self.source)
} else {
write!(f, "{}", self.name)
}
}
}
impl std::str::FromStr for RoleIdentifier {
type Err = RoleFromStrError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Some((name, source)) = split_once(s, '/') {
Ok(RoleIdentifier { name: name.to_string(), source: source.to_string() })
} else {
Ok(RoleIdentifier { name: s.to_string(), source: String::new() })
}
}
}
impl TryFrom<String> for RoleIdentifier {
type Error = RoleFromStrError;
fn try_from(s: String) -> std::result::Result<Self, Self::Error> {
<RoleIdentifier as std::str::FromStr>::from_str(&s)
}
}
impl Into<String> for RoleIdentifier {
fn into(self) -> String {
format!("{}", self)
}
}
impl RoleIdentifier {
pub fn local_from_str(source: String, name: String) -> Self {
RoleIdentifier { name, source }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RoleFromStrError {
/// No '@' or '%' found. That's strange, huh?
Invalid
}
impl fmt::Display for RoleFromStrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RoleFromStrError::Invalid
=> write!(f, "Rolename are of form 'name%source' or 'name@realm'."),
}
}
}

View File

@ -5,7 +5,7 @@ use crate::capnp::machinesystem::Machines;
use crate::capnp::permissionsystem::Permissions; use crate::capnp::permissionsystem::Permissions;
use crate::capnp::user_system::Users; use crate::capnp::user_system::Users;
use crate::session::{SessionHandle, SessionManager}; use crate::session::{SessionHandle, SessionManager};
use crate::users::User; use crate::users::UserRef;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct APISession; pub struct APISession;

View File

@ -9,7 +9,6 @@ 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::RoleIdentifier;
type Result<T> = std::result::Result<T, serde_dhall::Error>; type Result<T> = std::result::Result<T, serde_dhall::Error>;
@ -66,7 +65,7 @@ pub struct Config {
pub db_path: PathBuf, pub db_path: PathBuf,
pub auditlog_path: PathBuf, pub auditlog_path: PathBuf,
pub roles: HashMap<RoleIdentifier, RoleConfig>, pub roles: HashMap<String, RoleConfig>,
#[serde(flatten)] #[serde(flatten)]
pub tlsconfig: TlsListen, pub tlsconfig: TlsListen,
@ -90,7 +89,7 @@ impl Config {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleConfig { pub struct RoleConfig {
#[serde(default = "Vec::new")] #[serde(default = "Vec::new")]
pub parents: Vec<RoleIdentifier>, pub parents: Vec<String>,
#[serde(default = "Vec::new")] #[serde(default = "Vec::new")]
pub permissions: Vec<PermRule>, pub permissions: Vec<PermRule>,
} }

View File

@ -119,7 +119,7 @@ impl Diflouroborane {
let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?;
let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?;
let sessionmanager = SessionManager::new(); let sessionmanager = SessionManager::new(userdb.clone());
let authentication = AuthenticationHandle::new(userdb.clone()); let authentication = AuthenticationHandle::new(userdb.clone());
let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor, sessionmanager, authentication))?; let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor, sessionmanager, authentication))?;

View File

@ -9,7 +9,7 @@ use crate::resources::modules::fabaccess::{MachineState, Status};
use crate::resources::state::db::StateDB; use crate::resources::state::db::StateDB;
use crate::resources::state::State; use crate::resources::state::State;
use crate::session::SessionHandle; use crate::session::SessionHandle;
use crate::users::User; use crate::users::UserRef;
pub mod claim; pub mod claim;
pub mod db; pub mod db;
@ -173,7 +173,7 @@ impl Resource {
session.has_disclose(self) || self.is_owned_by(session.get_user()) session.has_disclose(self) || self.is_owned_by(session.get_user())
} }
pub fn is_owned_by(&self, owner: User) -> bool { pub fn is_owned_by(&self, owner: UserRef) -> bool {
match self.get_state().state { match self.get_state().state {
Status::Free | Status::Disabled => false, Status::Free | Status::Disabled => false,

View File

@ -9,7 +9,7 @@ use crate::oidvalue;
use crate::resources::state::{State}; use crate::resources::state::{State};
use crate::resources::state::value::Value; use crate::resources::state::value::Value;
use crate::session::SessionHandle; use crate::session::SessionHandle;
use crate::users::User; use crate::users::UserRef;
/// Status of a Machine /// Status of a Machine
#[derive( #[derive(
@ -28,15 +28,15 @@ pub enum Status {
/// Not currently used by anybody /// Not currently used by anybody
Free, Free,
/// Used by somebody /// Used by somebody
InUse(User), InUse(UserRef),
/// Was used by somebody and now needs to be checked for cleanliness /// Was used by somebody and now needs to be checked for cleanliness
ToCheck(User), ToCheck(UserRef),
/// Not used by anybody but also can not be used. E.g. down for maintenance /// Not used by anybody but also can not be used. E.g. down for maintenance
Blocked(User), Blocked(UserRef),
/// Disabled for some other reason /// Disabled for some other reason
Disabled, Disabled,
/// Reserved /// Reserved
Reserved(User), Reserved(UserRef),
} }
#[derive( #[derive(
@ -54,7 +54,7 @@ pub enum Status {
/// The status of the machine /// The status of the machine
pub struct MachineState { pub struct MachineState {
pub state: Status, pub state: Status,
pub previous: Option<User>, pub previous: Option<UserRef>,
} }
impl MachineState { impl MachineState {
@ -76,42 +76,42 @@ impl MachineState {
} }
} }
pub fn free(previous: Option<User>) -> Self { pub fn free(previous: Option<UserRef>) -> Self {
Self { Self {
state: Status::Free, state: Status::Free,
previous, previous,
} }
} }
pub fn used(user: User, previous: Option<User>) -> Self { pub fn used(user: UserRef, previous: Option<UserRef>) -> Self {
Self { Self {
state: Status::InUse(user), state: Status::InUse(user),
previous, previous,
} }
} }
pub fn blocked(user: User, previous: Option<User>) -> Self { pub fn blocked(user: UserRef, previous: Option<UserRef>) -> Self {
Self { Self {
state: Status::Blocked(user), state: Status::Blocked(user),
previous, previous,
} }
} }
pub fn disabled(previous: Option<User>) -> Self { pub fn disabled(previous: Option<UserRef>) -> Self {
Self { Self {
state: Status::Disabled, state: Status::Disabled,
previous, previous,
} }
} }
pub fn reserved(user: User, previous: Option<User>) -> Self { pub fn reserved(user: UserRef, previous: Option<UserRef>) -> Self {
Self { Self {
state: Status::Reserved(user), state: Status::Reserved(user),
previous, previous,
} }
} }
pub fn check(user: User) -> Self { pub fn check(user: UserRef) -> Self {
Self { Self {
state: Status::ToCheck(user.clone()), state: Status::ToCheck(user.clone()),
previous: Some(user), previous: Some(user),

37
bffhd/session/db.rs Normal file
View File

@ -0,0 +1,37 @@
use std::sync::Arc;
use lmdb::{DatabaseFlags, Environment};
use rkyv::{Archive, Serialize, Deserialize};
use crate::db::{AllocAdapter, DB, RawDB};
use crate::users::UserRef;
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Archive, Serialize, Deserialize)]
pub struct Session {
userid: UserRef,
}
type Adapter = AllocAdapter<Session>;
pub struct SessionCache {
env: Arc<Environment>,
db: DB<Adapter>,
}
impl SessionCache {
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
let db = DB::new_unchecked(db);
Self { env, db }
}
pub unsafe fn open(env: Arc<Environment>) -> lmdb::Result<Self> {
let db = RawDB::open(&env, Some("sessions"))?;
Ok(Self::new(env, db))
}
pub unsafe fn create(env: Arc<Environment>) -> lmdb::Result<Self> {
let flags = DatabaseFlags::empty();
let db = RawDB::create(&env, Some("sessions"), flags)?;
Ok(Self::new(env, db))
}
}

View File

@ -1,38 +1,46 @@
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Context;
use lmdb::Environment;
use once_cell::sync::OnceCell;
use crate::authorization::roles::Role; use crate::authorization::roles::Role;
use crate::resources::Resource; use crate::resources::Resource;
use crate::users::User; use crate::session::db::SessionCache;
use crate::Users;
use crate::users::UserRef;
struct Inner { mod db;
} static SESSION_CACHE: OnceCell<SessionCache> = OnceCell::new();
impl Inner {
pub fn new() -> Self {
Self { }
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct SessionManager { pub struct SessionManager {
inner: Arc<Inner>, users: Users,
} }
impl SessionManager { impl SessionManager {
pub fn new() -> Self { pub fn new(users: Users) -> Self {
Self { Self { users }
inner: Arc::new(Inner::new()),
}
} }
// TODO: make infallible
pub fn open(&self, uid: impl AsRef<str>) -> Option<SessionHandle> { pub fn open(&self, uid: impl AsRef<str>) -> Option<SessionHandle> {
unimplemented!() let uid = uid.as_ref();
if let Some(user) = self.users.get_user(uid) {
tracing::trace!(uid, "opening new session for user");
Some(SessionHandle { user: UserRef::new(user.id) })
} else {
None
}
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SessionHandle { pub struct SessionHandle {
user: UserRef,
} }
impl SessionHandle { impl SessionHandle {
pub fn get_user(&self) -> User { pub fn get_user(&self) -> UserRef {
unimplemented!() unimplemented!()
} }

View File

@ -4,9 +4,9 @@ use lmdb::{RwTransaction, Transaction};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Context;
use rkyv::{Archived, Deserialize}; use rkyv::{Archived, Deserialize};
use crate::authorization::roles::RoleIdentifier;
#[derive( #[derive(
Clone, Clone,
@ -24,6 +24,17 @@ pub struct User {
pub userdata: UserData, pub userdata: UserData,
} }
impl User {
pub fn check_password(&self, pwd: &[u8]) -> anyhow::Result<bool> {
if let Some(ref encoded) = self.userdata.passwd {
argon2::verify_encoded(encoded, pwd)
.context("Stored password is an invalid string")
} else {
Ok(false)
}
}
}
#[derive( #[derive(
Clone, Clone,
PartialEq, PartialEq,

View File

@ -26,7 +26,7 @@ use std::sync::Arc;
pub mod db; pub mod db;
pub use crate::authentication::db::PassDB; pub use crate::authentication::db::PassDB;
use crate::authorization::roles::{Role, RoleIdentifier}; use crate::authorization::roles::Role;
use crate::db::LMDBorrow; use crate::db::LMDBorrow;
use crate::users::db::UserData; use crate::users::db::UserData;
use crate::UserDB; use crate::UserDB;
@ -43,13 +43,13 @@ use crate::UserDB;
serde::Deserialize, serde::Deserialize,
)] )]
#[archive_attr(derive(Debug, PartialEq))] #[archive_attr(derive(Debug, PartialEq))]
pub struct User { pub struct UserRef {
id: String, id: String,
} }
impl User { impl UserRef {
pub fn new(id: String) -> Self { pub fn new(id: String) -> Self {
User { id } UserRef { id }
} }
pub fn get_username(&self) -> &str { pub fn get_username(&self) -> &str {
@ -73,7 +73,7 @@ impl Users {
let userdb = USERDB let userdb = USERDB
.get_or_try_init(|| { .get_or_try_init(|| {
tracing::debug!("Global resource not yet initialized, initializing…"); tracing::debug!("Global resource not yet initialized, initializing…");
unsafe { UserDB::create(env.clone()) } unsafe { UserDB::create(env) }
}) })
.context("Failed to open userdb")?; .context("Failed to open userdb")?;