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;
|
|
|
|
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:30:04 +01:00
|
|
|
use crate::error::Result;
|
|
|
|
|
2020-11-24 15:57:23 +01:00
|
|
|
mod internal;
|
|
|
|
|
|
|
|
#[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
|
2020-11-24 14:41:19 +01:00
|
|
|
uid: String,
|
|
|
|
/// 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
|
2020-11-24 15:57:23 +01:00
|
|
|
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
|
2020-11-24 15:57:23 +01:00
|
|
|
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,
|
|
|
|
|
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 {
|
|
|
|
Self {
|
|
|
|
roles: roles,
|
|
|
|
priority: priority,
|
|
|
|
kv: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)?;
|
|
|
|
|
|
|
|
Ok(HashMap::from_iter(map.drain().map(|(uid, user_data)|
|
|
|
|
( uid.clone()
|
|
|
|
, User::new(UserId::new(uid, None, None), user_data)
|
|
|
|
)
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
2020-12-01 16:06:39 +01:00
|
|
|
#[cfg(test_DISABLED)]
|
2020-11-24 14:41:19 +01:00
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn format_uid_test() {
|
|
|
|
let uid = "testuser".to_string();
|
|
|
|
let suid = "testsuid".to_string();
|
2020-11-24 15:57:23 +01:00
|
|
|
let realm = "testloc".to_string();
|
2020-10-26 12:58:55 +01:00
|
|
|
|
2020-11-24 14:41:19 +01:00
|
|
|
assert_eq!("testuser",
|
|
|
|
format!("{}", UserIdentifier::new(uid.clone(), None, None)));
|
|
|
|
assert_eq!("testuser+testsuid",
|
|
|
|
format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None)));
|
|
|
|
assert_eq!("testuser+testsuid",
|
|
|
|
format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None)));
|
|
|
|
assert_eq!("testuser+testsuid@testloc",
|
2020-11-24 15:57:23 +01:00
|
|
|
format!("{}", UserIdentifier::new(uid, Some(suid), Some(realm))));
|
2020-11-24 14:41:19 +01:00
|
|
|
}
|
2020-10-26 12:58:55 +01:00
|
|
|
}
|