mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-22 11:43:49 +01:00
Session initialization
This commit is contained in:
parent
2e9c7fbc19
commit
5c9b72c37d
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
@ -58,89 +84,4 @@ 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'."),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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))?;
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
37
bffhd/session/db.rs
Normal 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))
|
||||
}
|
||||
}
|
@ -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!()
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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")?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user