fabaccess-bffh/src/db/user.rs

143 lines
4.2 KiB
Rust
Raw Normal View History

2020-11-24 15:57:23 +01:00
//! UserDB does two kinds of lookups:
//! 1. "I have this here username, what user is that"
//! 2. "I have this here user, what are their roles (and other associated data)"
2020-10-26 12:58:55 +01:00
use serde::{Serialize, Deserialize};
use std::fmt;
2020-12-16 13:30:04 +01:00
use std::fs;
2020-12-16 13:51:47 +01:00
use std::sync::Arc;
2020-12-16 13:30:04 +01:00
use std::iter::FromIterator;
use std::path::Path;
2020-10-26 12:58:55 +01:00
use crate::db::access::RoleIdentifier;
2020-10-28 16:25:33 +01:00
use std::collections::HashMap;
2020-10-26 12:58:55 +01:00
2020-12-16 13:51:47 +01:00
use slog::Logger;
2020-12-16 13:30:04 +01:00
use crate::error::Result;
2020-12-16 13:51:47 +01:00
use crate::config::Config;
2020-12-16 13:30:04 +01:00
2020-11-24 15:57:23 +01:00
mod internal;
2020-12-16 13:51:47 +01:00
pub use internal::Internal;
2020-11-24 15:57:23 +01:00
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2020-11-30 07:23:47 +01:00
/// An user
2020-11-24 15:57:23 +01:00
pub struct User {
2020-11-30 07:23:47 +01:00
/// The precise (and unique) identifier of this user
2020-11-24 15:57:23 +01:00
pub id: UserId,
2020-11-30 07:23:47 +01:00
/// Data BFFH stores on this user to base decisions on
2020-11-24 15:57:23 +01:00
pub data: UserData,
}
2020-12-02 16:20:50 +01:00
impl User {
pub fn new(id: UserId, data: UserData) -> Self {
Self { id, data }
}
}
2020-11-24 14:41:19 +01:00
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
/// Authorization Identity
///
/// This identity is internal to FabAccess and completely independent from the authentication
/// method or source
2020-11-24 15:57:23 +01:00
pub struct UserId {
2020-11-30 07:23:47 +01:00
/// Main User ID. Generally an user name or similar. Locally unique
2021-09-19 22:53:43 +02:00
pub uid: String,
2020-11-24 14:41:19 +01:00
/// Sub user ID.
///
/// Can change scopes for permissions, e.g. having a +admin account with more permissions than
/// the default account and +dashboard et.al. accounts that have restricted permissions for
/// their applications
2021-09-19 22:53:43 +02:00
pub subuid: Option<String>,
2020-11-24 14:41:19 +01:00
/// Realm this account originates.
///
/// The Realm is usually described by a domain name but local policy may dictate an unrelated
/// mapping
2021-09-19 22:53:43 +02:00
pub realm: Option<String>,
2020-11-24 14:41:19 +01:00
}
2020-11-24 15:57:23 +01:00
impl UserId {
pub fn new(uid: String, subuid: Option<String>, realm: Option<String>) -> Self {
Self { uid, subuid, realm }
}
}
impl fmt::Display for UserId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let r = write!(f, "{}", self.uid);
if let Some(ref s) = self.subuid {
write!(f, "+{}", s)?;
}
if let Some(ref l) = self.realm {
write!(f, "@{}", l)?;
}
r
}
}
2020-10-26 12:58:55 +01:00
2020-11-24 15:57:23 +01:00
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
2020-11-30 07:23:47 +01:00
/// Data on an user to base decisions on
///
/// This of course includes authorization data, i.e. that users set roles
2020-11-24 15:57:23 +01:00
pub struct UserData {
2020-10-26 12:58:55 +01:00
/// A Person has N ≥ 0 roles.
/// Persons are only ever given roles, not permissions directly
2020-10-28 16:25:33 +01:00
pub roles: Vec<RoleIdentifier>,
2020-11-30 14:08:03 +01:00
#[serde(skip_serializing_if = "is_zero")]
#[serde(default = "default_priority")]
/// A priority number, defaulting to 0.
///
/// The higher, the higher the priority. Higher priority users overwrite lower priority ones.
pub priority: u64,
2021-09-20 13:47:08 +02:00
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub passwd: Option<String>,
2020-10-28 16:25:33 +01:00
/// Additional data storage
2020-12-16 13:30:04 +01:00
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
kv: HashMap<String, String>,
2020-10-26 12:58:55 +01:00
}
2020-12-02 16:20:50 +01:00
impl UserData {
pub fn new(roles: Vec<RoleIdentifier>, priority: u64) -> Self {
2021-09-20 13:47:08 +02:00
Self { roles, priority, kv: HashMap::new(), passwd: None }
2020-12-02 16:20:50 +01:00
}
}
2020-11-30 14:08:03 +01:00
fn is_zero(i: &u64) -> bool {
*i == 0
}
const fn default_priority() -> u64 {
0
}
2020-12-16 13:30:04 +01:00
pub fn load_file<P: AsRef<Path>>(path: P) -> Result<HashMap<String, User>> {
let f = fs::read(path)?;
let mut map: HashMap<String, UserData> = toml::from_slice(&f)?;
2021-09-21 07:48:19 +02:00
Ok(HashMap::from_iter(map.drain().map(|(uid, mut user_data)| {
user_data.passwd = user_data.passwd.map(|pw| if !pw.starts_with("$argon2") {
let config = argon2::Config::default();
let salt: [u8; 16] = rand::random();
let hash = argon2::hash_encoded(pw.as_bytes(), &salt, &config)
.expect(&format!("Failed to hash password for {}: ", uid));
println!("Hashed pw for {} to {}", uid, hash);
hash
} else {
pw
});
2020-12-16 13:30:04 +01:00
( uid.clone()
, User::new(UserId::new(uid, None, None), user_data)
)
2021-09-21 07:48:19 +02:00
})))
2020-12-16 13:30:04 +01:00
}
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
2020-12-16 13:51:47 +01:00
let mut flags = lmdb::DatabaseFlags::empty();
2021-09-20 13:47:08 +02:00
let db = env.create_db(Some("userdb"), flags)?;
2020-12-16 13:51:47 +01:00
debug!(&log, "Opened user db successfully.");
Ok(Internal::new(log, env, db))
}