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::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;
@ -14,12 +15,40 @@ struct Callback {
}
impl Callback {
pub fn new(users: Users) -> Self {
Self { users, }
Self { users }
}
}
impl rsasl::callback::Callback for Callback {
fn validate(&self, session: &mut SessionData, validation: Validation, mechanism: &Mechname) -> Result<(), SessionError> {
todo!()
fn validate(
&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,14 +70,20 @@ impl AuthenticationHandle {
pub fn new(userdb: Users) -> Self {
let mut rsasl = SASL::new();
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> {
Ok(self.inner.rsasl.server_start(mechanism)?)
}
pub fn list_available_mechs(&self) -> impl IntoIterator<Item=&Mechname> {
self.inner.rsasl.server_mech_list().into_iter().map(|m| m.mechanism)
pub fn list_available_mechs(&self) -> impl IntoIterator<Item = &Mechname> {
self.inner
.rsasl
.server_mech_list()
.into_iter()
.map(|m| m.mechanism)
}
}

View File

@ -1,34 +1,26 @@
use std::sync::Arc;
use crate::authorization::permissions::Permission;
use crate::authorization::roles::Role;
use crate::authorization::roles::{Role, Roles};
use crate::Users;
use crate::users::User;
use crate::users::UserRef;
pub mod permissions;
pub mod roles;
struct Inner {
users: Users,
}
impl Inner {
pub fn new(users: Users) -> Self {
Self { users }
}
}
#[derive(Clone)]
pub struct AuthorizationHandle {
users: Users,
roles: Roles,
}
impl AuthorizationHandle {
pub fn new(users: Users) -> Self {
Self { users }
pub fn new(users: Users, roles: Roles) -> Self {
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())?;
Some([])
Some(user.userdata.roles.clone())
}
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 once_cell::sync::OnceCell;
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
///
/// 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
/// 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>,
parents: Vec<String>,
// If a role doesn't define permissions, default to an empty Vec.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
@ -30,7 +56,7 @@ pub struct 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 }
}
}
@ -59,88 +85,3 @@ impl fmt::Display for Role {
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::user_system::Users;
use crate::session::{SessionHandle, SessionManager};
use crate::users::User;
use crate::users::UserRef;
#[derive(Debug, Clone)]
pub struct APISession;

View File

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

View File

@ -119,7 +119,7 @@ impl Diflouroborane {
let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?;
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 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::State;
use crate::session::SessionHandle;
use crate::users::User;
use crate::users::UserRef;
pub mod claim;
pub mod db;
@ -173,7 +173,7 @@ impl Resource {
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 {
Status::Free | Status::Disabled => false,

View File

@ -9,7 +9,7 @@ use crate::oidvalue;
use crate::resources::state::{State};
use crate::resources::state::value::Value;
use crate::session::SessionHandle;
use crate::users::User;
use crate::users::UserRef;
/// Status of a Machine
#[derive(
@ -28,15 +28,15 @@ pub enum Status {
/// Not currently used by anybody
Free,
/// Used by somebody
InUse(User),
InUse(UserRef),
/// 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
Blocked(User),
Blocked(UserRef),
/// Disabled for some other reason
Disabled,
/// Reserved
Reserved(User),
Reserved(UserRef),
}
#[derive(
@ -54,7 +54,7 @@ pub enum Status {
/// The status of the machine
pub struct MachineState {
pub state: Status,
pub previous: Option<User>,
pub previous: Option<UserRef>,
}
impl MachineState {
@ -76,42 +76,42 @@ impl MachineState {
}
}
pub fn free(previous: Option<User>) -> Self {
pub fn free(previous: Option<UserRef>) -> Self {
Self {
state: Status::Free,
previous,
}
}
pub fn used(user: User, previous: Option<User>) -> Self {
pub fn used(user: UserRef, previous: Option<UserRef>) -> Self {
Self {
state: Status::InUse(user),
previous,
}
}
pub fn blocked(user: User, previous: Option<User>) -> Self {
pub fn blocked(user: UserRef, previous: Option<UserRef>) -> Self {
Self {
state: Status::Blocked(user),
previous,
}
}
pub fn disabled(previous: Option<User>) -> Self {
pub fn disabled(previous: Option<UserRef>) -> Self {
Self {
state: Status::Disabled,
previous,
}
}
pub fn reserved(user: User, previous: Option<User>) -> Self {
pub fn reserved(user: UserRef, previous: Option<UserRef>) -> Self {
Self {
state: Status::Reserved(user),
previous,
}
}
pub fn check(user: User) -> Self {
pub fn check(user: UserRef) -> Self {
Self {
state: Status::ToCheck(user.clone()),
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 anyhow::Context;
use lmdb::Environment;
use once_cell::sync::OnceCell;
use crate::authorization::roles::Role;
use crate::resources::Resource;
use crate::users::User;
use crate::session::db::SessionCache;
use crate::Users;
use crate::users::UserRef;
struct Inner {
mod db;
}
impl Inner {
pub fn new() -> Self {
Self { }
}
}
static SESSION_CACHE: OnceCell<SessionCache> = OnceCell::new();
#[derive(Clone)]
pub struct SessionManager {
inner: Arc<Inner>,
users: Users,
}
impl SessionManager {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner::new()),
}
pub fn new(users: Users) -> Self {
Self { users }
}
// TODO: make infallible
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)]
pub struct SessionHandle {
user: UserRef,
}
impl SessionHandle {
pub fn get_user(&self) -> User {
pub fn get_user(&self) -> UserRef {
unimplemented!()
}

View File

@ -4,9 +4,9 @@ use lmdb::{RwTransaction, Transaction};
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::sync::Arc;
use anyhow::Context;
use rkyv::{Archived, Deserialize};
use crate::authorization::roles::RoleIdentifier;
#[derive(
Clone,
@ -24,6 +24,17 @@ pub struct User {
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(
Clone,
PartialEq,

View File

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