mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-24 23:57:57 +01:00
All the things.
This commit is contained in:
parent
a7754f057b
commit
9e244aab7e
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -70,6 +70,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -208,6 +214,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
version = "2.0.0-dev"
|
version = "2.0.0-dev"
|
||||||
@ -247,6 +259,17 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2b_simd"
|
||||||
|
version = "0.5.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"constant_time_eq",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@ -450,6 +473,12 @@ dependencies = [
|
|||||||
"cache-padded",
|
"cache-padded",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -550,6 +579,7 @@ dependencies = [
|
|||||||
"rkyv_dyn",
|
"rkyv_dyn",
|
||||||
"rkyv_typename",
|
"rkyv_typename",
|
||||||
"rsasl",
|
"rsasl",
|
||||||
|
"rust-argon2",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_dhall",
|
"serde_dhall",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -1641,6 +1671,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-argon2"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"blake2b_simd",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -70,8 +70,8 @@ async-trait = "0.1.51"
|
|||||||
|
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
#rust-argon2 = "0.8.3"
|
rust-argon2 = "0.8.3"
|
||||||
#rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
capnpc = "0.14.4"
|
capnpc = "0.14.4"
|
||||||
@ -82,7 +82,6 @@ walkdir = "2.3.2"
|
|||||||
futures-test = "0.3.16"
|
futures-test = "0.3.16"
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
bincode = "2.0.0-dev"
|
bincode = "2.0.0-dev"
|
||||||
rand = "0.8"
|
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
bincode = { git = "https://github.com/dequbed/bincode.git", branch = "feature/in_place_buffer" }
|
bincode = { git = "https://github.com/dequbed/bincode.git", branch = "feature/in_place_buffer" }
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
, "notahostandnoport"
|
, "notahostandnoport"
|
||||||
]
|
]
|
||||||
, machines = ./machines.dhall
|
, machines = ./machines.dhall
|
||||||
, db_path = "/tmp/bffh"
|
, db_path = "/tmp/bffh/"
|
||||||
, roles = ./roles.dhall
|
, roles = ./roles.dhall
|
||||||
, mqtt_url = "tcp://localhost:1883"
|
, mqtt_url = "tcp://localhost:1883"
|
||||||
}
|
}
|
||||||
|
115
src/bin/bffhd.rs
115
src/bin/bffhd.rs
@ -6,6 +6,7 @@ use std::{
|
|||||||
use clap::{App, Arg, crate_version, crate_description, crate_name};
|
use clap::{App, Arg, crate_version, crate_description, crate_name};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use diflouroborane::{config, error::Error};
|
use diflouroborane::{config, error::Error};
|
||||||
|
use diflouroborane::db::{Databases, Dump};
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
|
|
||||||
fn main_res() -> Result<(), Error> {
|
fn main_res() -> Result<(), Error> {
|
||||||
@ -88,66 +89,66 @@ fn main_res() -> Result<(), Error> {
|
|||||||
|
|
||||||
println!("Final listens: {:?}", sockaddrs);
|
println!("Final listens: {:?}", sockaddrs);
|
||||||
|
|
||||||
/*
|
let dbs = Databases::create(config.db_path)?;
|
||||||
|
|
||||||
if matches.is_present("dump") {
|
if matches.is_present("dump") {
|
||||||
let db = db::Databases::new(&log, &config)?;
|
let dump = Dump::new(&dbs)?;
|
||||||
let v = db.access.dump_roles().unwrap();
|
let encoded = serde_json::to_vec(&dump).unwrap();
|
||||||
for (id, role) in v.iter() {
|
|
||||||
tracing::info!("Role {}:\n{}", id, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
let v = db.userdb.list_users()?;
|
// Direct writing to fd 1 is faster but also prevents any print-formatting that could
|
||||||
for user in v.iter() {
|
// invalidate the generated TOML
|
||||||
tracing::info!("User {}:\n{:?}", user.id, user.data);
|
let stdout = io::stdout();
|
||||||
}
|
let mut handle = stdout.lock();
|
||||||
Ok(())
|
handle.write_all(&encoded).unwrap();
|
||||||
} else if matches.is_present("load") {
|
|
||||||
let db = db::Databases::new(&log, &config)?;
|
|
||||||
let mut dir = PathBuf::from(matches.value_of_os("load").unwrap());
|
|
||||||
|
|
||||||
dir.push("users.toml");
|
|
||||||
let map = db::user::load_file(&dir)?;
|
|
||||||
for (uid,user) in map.iter() {
|
|
||||||
db.userdb.put_user(uid, user)?;
|
|
||||||
}
|
|
||||||
tracing::debug!("Loaded users: {:?}", map);
|
|
||||||
dir.pop();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let ex = smol::Executor::new();
|
|
||||||
let db = db::Databases::new(&log, &config)?;
|
|
||||||
|
|
||||||
let machines = machine::load(&config)?;
|
|
||||||
let (actor_map, actors) = actor::load(&log, &config)?;
|
|
||||||
let (init_map, initiators) = initiator::load(&log, &config, db.userdb.clone(), db.access.clone())?;
|
|
||||||
|
|
||||||
let mut network = network::Network::new(machines, actor_map, init_map);
|
|
||||||
|
|
||||||
for (a,b) in config.actor_connections.iter() {
|
|
||||||
if let Err(e) = network.connect_actor(a,b) {
|
|
||||||
tracing::error!("{}", e);
|
|
||||||
}
|
|
||||||
tracing::info!("[Actor] Connected {} to {}", a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (a,b) in config.init_connections.iter() {
|
|
||||||
if let Err(e) = network.connect_init(a,b) {
|
|
||||||
tracing::error!("{}", e);
|
|
||||||
}
|
|
||||||
tracing::info!("[Initi] Connected {} to {}", a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
for actor in actors.into_iter() {
|
|
||||||
ex.spawn(actor).detach();
|
|
||||||
}
|
|
||||||
for init in initiators.into_iter() {
|
|
||||||
ex.spawn(init).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
server::serve_api_connections(log.clone(), config, db, network, ex)
|
|
||||||
}
|
}
|
||||||
*/
|
/*
|
||||||
|
} else if matches.is_present("load") {
|
||||||
|
let db = db::Databases::new(&log, &config)?;
|
||||||
|
let mut dir = PathBuf::from(matches.value_of_os("load").unwrap());
|
||||||
|
|
||||||
|
dir.push("users.toml");
|
||||||
|
let map = db::user::load_file(&dir)?;
|
||||||
|
for (uid,user) in map.iter() {
|
||||||
|
db.userdb.put_user(uid, user)?;
|
||||||
|
}
|
||||||
|
tracing::debug!("Loaded users: {:?}", map);
|
||||||
|
dir.pop();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let ex = smol::Executor::new();
|
||||||
|
let db = db::Databases::new(&log, &config)?;
|
||||||
|
|
||||||
|
let machines = machine::load(&config)?;
|
||||||
|
let (actor_map, actors) = actor::load(&log, &config)?;
|
||||||
|
let (init_map, initiators) = initiator::load(&log, &config, db.userdb.clone(), db.access.clone())?;
|
||||||
|
|
||||||
|
let mut network = network::Network::new(machines, actor_map, init_map);
|
||||||
|
|
||||||
|
for (a,b) in config.actor_connections.iter() {
|
||||||
|
if let Err(e) = network.connect_actor(a,b) {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
tracing::info!("[Actor] Connected {} to {}", a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (a,b) in config.init_connections.iter() {
|
||||||
|
if let Err(e) = network.connect_init(a,b) {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
tracing::info!("[Initi] Connected {} to {}", a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for actor in actors.into_iter() {
|
||||||
|
ex.spawn(actor).detach();
|
||||||
|
}
|
||||||
|
for init in initiators.into_iter() {
|
||||||
|
ex.spawn(init).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
server::serve_api_connections(log.clone(), config, db, network, ex)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ impl<'de> serde::de::Visitor<'de> for ListenVisitor {
|
|||||||
type Value = Listen;
|
type Value = Listen;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
write!(formatter, "A string encoding a valid IP or Hostname (e.g. 127.0.0.1 or [::1]) with\
|
write!(formatter, "A string encoding a valid IP or Hostname (e.g. 127.0.0.1 or [::1]) with \
|
||||||
or without a defined port")
|
or without a defined port")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
|
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
|
||||||
|
@ -3,7 +3,7 @@ use std::{
|
|||||||
ops::Deref,
|
ops::Deref,
|
||||||
};
|
};
|
||||||
|
|
||||||
use lmdb::Transaction;
|
use crate::db::Transaction;
|
||||||
|
|
||||||
/// Memory Fixpoint for a value in the DB
|
/// Memory Fixpoint for a value in the DB
|
||||||
///
|
///
|
||||||
@ -22,11 +22,12 @@ pub struct LMDBorrow<T, V> {
|
|||||||
impl<'env, T, V> LMDBorrow<T, V>
|
impl<'env, T, V> LMDBorrow<T, V>
|
||||||
where T: Transaction,
|
where T: Transaction,
|
||||||
{
|
{
|
||||||
pub unsafe fn reborrow(ptr: &'_ V) -> NonNull<V> {
|
|
||||||
ptr.into()
|
|
||||||
}
|
|
||||||
pub unsafe fn new(ptr: NonNull<V>, txn: T) -> Self {
|
pub unsafe fn new(ptr: NonNull<V>, txn: T) -> Self {
|
||||||
Self { ptr: ptr.into(), txn, }
|
Self { ptr: ptr.into(), txn }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_txn(self) -> T {
|
||||||
|
self.txn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,4 +40,4 @@ impl<'env, T, V> Deref for LMDBorrow<T, V>
|
|||||||
// valid pointer so this is safe.
|
// valid pointer so this is safe.
|
||||||
unsafe { self.ptr.as_ref() }
|
unsafe { self.ptr.as_ref() }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,83 +0,0 @@
|
|||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
use crate::db::user::UserId;
|
|
||||||
|
|
||||||
pub mod internal;
|
|
||||||
use internal::Internal;
|
|
||||||
|
|
||||||
pub type MachineIdentifier = String;
|
|
||||||
pub type Priority = u64;
|
|
||||||
|
|
||||||
/// Status of a Machine
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Status {
|
|
||||||
/// Not currently used by anybody
|
|
||||||
Free,
|
|
||||||
/// Used by somebody
|
|
||||||
InUse(Option<UserId>),
|
|
||||||
/// Was used by somebody and now needs to be checked for cleanliness
|
|
||||||
ToCheck(UserId),
|
|
||||||
/// Not used by anybody but also can not be used. E.g. down for maintenance
|
|
||||||
Blocked(UserId),
|
|
||||||
/// Disabled for some other reason
|
|
||||||
Disabled,
|
|
||||||
/// Reserved
|
|
||||||
Reserved(UserId),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
/// The status of the machine
|
|
||||||
pub struct MachineState {
|
|
||||||
pub state: Status,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MachineState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { state: Status::Free }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from(state: Status) -> Self {
|
|
||||||
Self { state }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free() -> Self {
|
|
||||||
Self { state: Status::Free }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn used(uid: Option<UserId>) -> Self {
|
|
||||||
Self { state: Status::InUse(uid) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blocked(uid: UserId) -> Self {
|
|
||||||
Self { state: Status::Blocked(uid) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disabled() -> Self {
|
|
||||||
Self { state: Status::Disabled }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reserved(uid: UserId) -> Self {
|
|
||||||
Self { state: Status::Reserved(uid) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check(uid: UserId) -> Self {
|
|
||||||
Self { state: Status::ToCheck(uid) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
|
|
||||||
let mut flags = lmdb::DatabaseFlags::empty();
|
|
||||||
//flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
|
|
||||||
let machdb = env.create_db(Some("machines"), flags)?;
|
|
||||||
debug!(&log, "Opened machine db successfully.");
|
|
||||||
|
|
||||||
Ok(Internal::new(log, env, machdb))
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
|
||||||
|
|
||||||
use super::{MachineIdentifier, MachineState};
|
|
||||||
use crate::error::Result;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Internal {
|
|
||||||
log: Logger,
|
|
||||||
env: Arc<Environment>,
|
|
||||||
db: lmdb::Database,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Internal {
|
|
||||||
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
|
|
||||||
Self { log, env, db }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_with_txn<T: Transaction>(&self, txn: &T, id: &String)
|
|
||||||
-> Result<Option<MachineState>>
|
|
||||||
{
|
|
||||||
match txn.get(self.db, &id.as_bytes()) {
|
|
||||||
Ok(bytes) => {
|
|
||||||
let machine: MachineState = flexbuffers::from_slice(bytes)?;
|
|
||||||
Ok(Some(machine))
|
|
||||||
},
|
|
||||||
Err(lmdb::Error::NotFound) => { Ok(None) },
|
|
||||||
Err(e) => { Err(e.into()) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, id: &MachineIdentifier) -> Result<Option<MachineState>> {
|
|
||||||
let txn = self.env.begin_ro_txn()?;
|
|
||||||
self.get_with_txn(&txn, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &String, status: &MachineState)
|
|
||||||
-> Result<()>
|
|
||||||
{
|
|
||||||
let bytes = flexbuffers::to_vec(status)?;
|
|
||||||
txn.put(self.db, &uuid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put(&self, id: &MachineIdentifier, status: &MachineState) -> Result<()> {
|
|
||||||
let mut txn = self.env.begin_rw_txn()?;
|
|
||||||
self.put_with_txn(&mut txn, id, status)?;
|
|
||||||
txn.commit().map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter<T: Transaction>(&self, txn: &T) -> Result<impl Iterator<Item=MachineState>> {
|
|
||||||
let mut cursor = txn.open_ro_cursor(self.db)?;
|
|
||||||
Ok(cursor.iter_start().map(|buf| {
|
|
||||||
let (_kbuf, vbuf) = buf.unwrap();
|
|
||||||
flexbuffers::from_slice(vbuf).unwrap()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ mod typed;
|
|||||||
// re-exports
|
// re-exports
|
||||||
pub use typed::{
|
pub use typed::{
|
||||||
DB,
|
DB,
|
||||||
Cursor,
|
TypedCursor,
|
||||||
|
|
||||||
Adapter,
|
Adapter,
|
||||||
OutputBuffer,
|
OutputBuffer,
|
||||||
@ -49,8 +49,25 @@ pub use resources::{
|
|||||||
ResourceDB,
|
ResourceDB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod pass;
|
||||||
|
pub use pass::{
|
||||||
|
PassDB,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod user;
|
||||||
|
pub use user::{
|
||||||
|
UserDB,
|
||||||
|
};
|
||||||
|
|
||||||
use lmdb::Error;
|
use lmdb::Error;
|
||||||
use rkyv::ser::serializers::AlignedSerializer;
|
use rkyv::ser::serializers::AlignedSerializer;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::path::Path;
|
||||||
|
use crate::db::user::User;
|
||||||
|
use crate::db::resources::Resource;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::state::State;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DBError {
|
pub enum DBError {
|
||||||
@ -58,6 +75,8 @@ pub enum DBError {
|
|||||||
RKYV(<AllocSerializer<1024> as Fallible>::Error),
|
RKYV(<AllocSerializer<1024> as Fallible>::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type Result<T> = std::result::Result<T, DBError>;
|
||||||
|
|
||||||
impl From<lmdb::Error> for DBError {
|
impl From<lmdb::Error> for DBError {
|
||||||
fn from(e: lmdb::Error) -> Self {
|
fn from(e: lmdb::Error) -> Self {
|
||||||
Self::LMDB(e)
|
Self::LMDB(e)
|
||||||
@ -112,4 +131,59 @@ impl<V: Serialize<AlignedSerializer<AlignedVec>>> Adapter for AlignedAdapter<V>
|
|||||||
fn from_db_err(e: Error) -> <Self as Fallible>::Error {
|
fn from_db_err(e: Error) -> <Self as Fallible>::Error {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Databases {
|
||||||
|
pub userdb: UserDB,
|
||||||
|
pub passdb: PassDB,
|
||||||
|
pub resourcedb: ResourceDB,
|
||||||
|
pub statedb: StateDB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Databases {
|
||||||
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
|
let env = Arc::new(Environment::new()
|
||||||
|
.open(&Path::join(path.as_ref(), "internal"))?
|
||||||
|
);
|
||||||
|
let userdb = unsafe { UserDB::open(env.clone())? };
|
||||||
|
let passdb = unsafe { PassDB::open(env.clone())? };
|
||||||
|
let resourcedb = unsafe { ResourceDB::open(env)? };
|
||||||
|
|
||||||
|
let statedb = StateDB::open(&Path::join(path.as_ref(), "state"))?;
|
||||||
|
|
||||||
|
Ok(Self { userdb, passdb, resourcedb, statedb })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
|
let env = Arc::new(Environment::new()
|
||||||
|
.set_max_dbs(16)
|
||||||
|
.open(path.as_ref())?
|
||||||
|
);
|
||||||
|
let userdb = unsafe { UserDB::create(env.clone())? };
|
||||||
|
let passdb = unsafe { PassDB::create(env.clone())? };
|
||||||
|
let resourcedb = unsafe { ResourceDB::create(env)? };
|
||||||
|
|
||||||
|
let statedb = StateDB::create(&Path::join(path.as_ref(), "state"))?;
|
||||||
|
|
||||||
|
Ok(Self { userdb, passdb, resourcedb, statedb })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Dump {
|
||||||
|
users: HashMap<String, User>,
|
||||||
|
passwds: HashMap<String, String>,
|
||||||
|
resources: HashMap<String, Resource>,
|
||||||
|
states: HashMap<String, (State, State)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dump {
|
||||||
|
pub fn new(dbs: &Databases) -> Result<Self> {
|
||||||
|
let users = HashMap::from_iter(dbs.userdb.get_all()?.into_iter());
|
||||||
|
let passwds = HashMap::new();
|
||||||
|
let resources = HashMap::new();
|
||||||
|
let states = HashMap::new();
|
||||||
|
|
||||||
|
Ok(Self { users, passwds, resources, states })
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,78 +1,58 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::path::Path;
|
use super::Environment;
|
||||||
use std::fs;
|
use super::AllocAdapter;
|
||||||
use std::collections::HashMap;
|
use super::DB;
|
||||||
|
use super::raw::RawDB;
|
||||||
|
use super::{DatabaseFlags, WriteFlags};
|
||||||
|
use crate::db::Result;
|
||||||
|
use super::Transaction;
|
||||||
|
|
||||||
use argon2;
|
use argon2;
|
||||||
use lmdb::{Environment, Transaction, RwTransaction};
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
|
|
||||||
|
type Adapter = AllocAdapter<String>;
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct PassDB {
|
pub struct PassDB {
|
||||||
log: Logger,
|
|
||||||
env: Arc<Environment>,
|
env: Arc<Environment>,
|
||||||
db: lmdb::Database,
|
db: DB<Adapter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PassDB {
|
impl PassDB {
|
||||||
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
|
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
||||||
Self { log, env, db }
|
let db = DB::new_unchecked(db);
|
||||||
|
Self { env, db }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(log: Logger, env: Arc<Environment>) -> Result<Self> {
|
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
|
||||||
let mut flags = lmdb::DatabaseFlags::empty();
|
let db = RawDB::open(&env, Some("pass"))?;
|
||||||
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
|
Ok(Self::new(env, db))
|
||||||
let db = env.create_db(Some("pass"), flags)?;
|
|
||||||
|
|
||||||
Ok(Self::new(log, env, db))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check a password for a given authcid.
|
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
|
||||||
///
|
let flags = DatabaseFlags::empty();
|
||||||
/// `Ok(None)` means the given authcid is not stored in the database
|
let db = RawDB::create(&env, Some("pass"), flags)?;
|
||||||
pub fn check_with_txn<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
Ok(Self::new(env, db))
|
||||||
match txn.get(self.db, &authcid.as_bytes()) {
|
|
||||||
Ok(bytes) => {
|
|
||||||
let encoded = unsafe { std::str::from_utf8_unchecked(bytes) };
|
|
||||||
let res = argon2::verify_encoded(encoded, password)?;
|
|
||||||
Ok(Some(res))
|
|
||||||
},
|
|
||||||
Err(lmdb::Error::NotFound) => { Ok(None) },
|
|
||||||
Err(e) => { Err(e.into()) },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn check(&self, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
|
||||||
|
pub fn check_pw<P: AsRef<[u8]>>(&self, uid: &str, inpass: P) -> Result<Option<bool>> {
|
||||||
let txn = self.env.begin_ro_txn()?;
|
let txn = self.env.begin_ro_txn()?;
|
||||||
self.check_with_txn(&txn, authcid, password)
|
if let Some(pass) = self.db.get(&txn, &uid.as_bytes())? {
|
||||||
}
|
Ok(argon2::verify_encoded(pass.as_str(), inpass.as_ref())
|
||||||
|
.ok())
|
||||||
/// Store a password for a given authcid, potentially overwriting an existing password
|
} else {
|
||||||
pub fn store_with_txn(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> {
|
Ok(None)
|
||||||
let config = argon2::Config::default();
|
|
||||||
let salt: [u8; 16] = rand::random();
|
|
||||||
let hash = argon2::hash_encoded(password, &salt, &config)?;
|
|
||||||
txn.put(self.db, &authcid.as_bytes(), &hash.as_bytes(), lmdb::WriteFlags::empty())
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_multiple(&self, vec: Vec<(String, String)>) -> Result<()> {
|
|
||||||
let mut txn = self.env.begin_rw_txn()?;
|
|
||||||
for (authcid, password) in vec.iter() {
|
|
||||||
self.store_with_txn(&mut txn, authcid.as_ref(), password.as_bytes())?;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_password<P: AsRef<[u8]>>(&self, uid: &str, password: P) -> Result<()> {
|
||||||
|
let cfg = argon2::Config::default();
|
||||||
|
let salt: [u8; 10] = rand::random();
|
||||||
|
let enc = argon2::hash_encoded(password.as_ref(), &salt, &cfg)
|
||||||
|
.expect("Hashing password failed for static valid config");
|
||||||
|
|
||||||
|
let flags = WriteFlags::empty();
|
||||||
|
let mut txn = self.env.begin_rw_txn()?;
|
||||||
|
self.db.put(&mut txn, &uid.as_bytes(), &enc, flags)?;
|
||||||
txn.commit()?;
|
txn.commit()?;
|
||||||
|
|
||||||
let v: Vec<&String> = vec.iter().map(|(a,_)| a).collect();
|
|
||||||
debug!(self.log, "Loaded passwords for: {:?}", v);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn load_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
|
||||||
let f = fs::read(path)?;
|
|
||||||
let mut map: HashMap<String, String> = toml::from_slice(&f)?;
|
|
||||||
|
|
||||||
self.insert_multiple(map.drain().collect())
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,10 +7,12 @@ use super::{
|
|||||||
use crate::db::AlignedAdapter;
|
use crate::db::AlignedAdapter;
|
||||||
use crate::db::raw::RawDB;
|
use crate::db::raw::RawDB;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use lmdb::Environment;
|
use crate::db::{Environment, DatabaseFlags};
|
||||||
use crate::db;
|
use crate::db::Result;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
#[derive(Archive, Serialize, Deserialize)]
|
#[derive(Archive, Serialize, Deserialize)]
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Resource {
|
pub struct Resource {
|
||||||
uuid: u128,
|
uuid: u128,
|
||||||
id: String,
|
id: String,
|
||||||
@ -18,6 +20,7 @@ pub struct Resource {
|
|||||||
description_idx: u64,
|
description_idx: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ResourceDB {
|
pub struct ResourceDB {
|
||||||
env: Arc<Environment>,
|
env: Arc<Environment>,
|
||||||
db: DB<AllocAdapter<Resource>>,
|
db: DB<AllocAdapter<Resource>>,
|
||||||
@ -32,10 +35,24 @@ impl ResourceDB {
|
|||||||
Self { env, db, id_index }
|
Self { env, db, id_index }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_id<S: AsRef<str>>(&self, id: S) -> Result<Option<u64>, db::Error> {
|
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
|
||||||
|
let db = RawDB::open(&env, Some("resources"))?;
|
||||||
|
let idx = RawDB::open(&env, Some("resources-idx"))?;
|
||||||
|
Ok(Self::new(env, db, idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
|
||||||
|
let flags = DatabaseFlags::empty();
|
||||||
|
let db = RawDB::create(&env, Some("resources"), flags)?;
|
||||||
|
let idx = RawDB::create(&env, Some("resources-idx"), flags)?;
|
||||||
|
Ok(Self::new(env, db, idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_id<S: AsRef<str>>(&self, id: S) -> Result<Option<u64>> {
|
||||||
let txn = self.env.begin_ro_txn()?;
|
let txn = self.env.begin_ro_txn()?;
|
||||||
self.id_index.get(&txn, &id.as_ref().as_bytes()).map(|ok| {
|
let id = self.id_index.get(&txn, &id.as_ref().as_bytes()).map(|ok| {
|
||||||
ok.map(|num| *num)
|
ok.map(|num| *num)
|
||||||
})
|
})?;
|
||||||
|
Ok(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -75,6 +75,15 @@ impl StateDB {
|
|||||||
Ok(Self::new(env, input, output))
|
Ok(Self::new(env, input, output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
|
||||||
|
let flags = DatabaseFlags::empty();
|
||||||
|
let env = Self::open_env(path)?;
|
||||||
|
let input = unsafe { DB::create(&env, Some("input"), flags)? };
|
||||||
|
let output = unsafe { DB::create(&env, Some("output"), flags)? };
|
||||||
|
|
||||||
|
Ok(Self::new(env, input, output))
|
||||||
|
}
|
||||||
|
|
||||||
fn update_txn(&self, txn: &mut RwTransaction, key: u64, input: &State, output: &State)
|
fn update_txn(&self, txn: &mut RwTransaction, key: u64, input: &State, output: &State)
|
||||||
-> Result<(), DBError>
|
-> Result<(), DBError>
|
||||||
{
|
{
|
||||||
|
@ -68,6 +68,7 @@ impl<const N: usize> OutputBuffer for AllocSerializer<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This should be possible to autoimplement for Sized Serializers
|
||||||
pub trait OutputWriter: Fallible {
|
pub trait OutputWriter: Fallible {
|
||||||
fn write_into(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>;
|
fn write_into(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
@ -141,13 +142,13 @@ impl<A: Adapter> DB<A>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_ro_cursor<'txn, T: Transaction>(&self, txn: &'txn T)
|
pub fn open_ro_cursor<'txn, T: Transaction>(&self, txn: &'txn T)
|
||||||
-> Result<Cursor<lmdb::RoCursor<'txn>, A>, A::Error>
|
-> Result<TypedCursor<lmdb::RoCursor<'txn>, A>, A::Error>
|
||||||
{
|
{
|
||||||
let c = self.db.open_ro_cursor(txn)
|
let c = self.db.open_ro_cursor(txn)
|
||||||
.map_err(A::from_db_err)?;
|
.map_err(A::from_db_err)?;
|
||||||
// Safe because we are providing both Adapter and cursor and know it matches
|
// Safe because we are providing both Adapter and cursor and know it matches
|
||||||
Ok(unsafe { Cursor::new(c) })
|
Ok(unsafe { TypedCursor::new(c) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +191,12 @@ impl<'a, A> DB<A>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cursor<C, A> {
|
pub struct TypedCursor<C, A> {
|
||||||
cursor: C,
|
cursor: C,
|
||||||
phantom: PhantomData<A>,
|
phantom: PhantomData<A>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'txn, C, A> Cursor<C, A>
|
impl<'txn, C, A> TypedCursor<C, A>
|
||||||
where C: lmdb::Cursor<'txn>,
|
where C: lmdb::Cursor<'txn>,
|
||||||
A: Adapter,
|
A: Adapter,
|
||||||
{
|
{
|
||||||
|
181
src/db/user.rs
181
src/db/user.rs
@ -1,142 +1,71 @@
|
|||||||
//! 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)"
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use std::fmt;
|
|
||||||
use std::fs;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::iter::FromIterator;
|
use super::{DB, AllocAdapter, Environment, Result};
|
||||||
use std::path::Path;
|
use crate::db::raw::RawDB;
|
||||||
use crate::db::access::RoleIdentifier;
|
use crate::db::{DatabaseFlags, LMDBorrow, RoTransaction, WriteFlags, };
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use slog::Logger;
|
use rkyv::{Archive, Serialize, Deserialize, Archived};
|
||||||
|
|
||||||
use crate::error::Result;
|
type Adapter = AllocAdapter<User>;
|
||||||
use crate::config::Config;
|
#[derive(Clone)]
|
||||||
|
pub struct UserDB {
|
||||||
|
env: Arc<Environment>,
|
||||||
|
db: DB<Adapter>,
|
||||||
|
}
|
||||||
|
|
||||||
mod internal;
|
#[derive(Debug, Clone, Archive, Serialize, Deserialize, serde::Serialize, serde::Deserialize)]
|
||||||
pub use internal::Internal;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
/// An user
|
|
||||||
pub struct User {
|
pub struct User {
|
||||||
/// The precise (and unique) identifier of this user
|
id: u128,
|
||||||
pub id: UserId,
|
username: String,
|
||||||
/// Data BFFH stores on this user to base decisions on
|
roles: Vec<String>,
|
||||||
pub data: UserData,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl UserDB {
|
||||||
pub fn new(id: UserId, data: UserData) -> Self {
|
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
||||||
Self { id, data }
|
let db = DB::new_unchecked(db);
|
||||||
|
Self { env, db }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
|
||||||
/// Authorization Identity
|
let db = RawDB::open(&env, Some("user"))?;
|
||||||
///
|
Ok(Self::new(env, db))
|
||||||
/// This identity is internal to FabAccess and completely independent from the authentication
|
|
||||||
/// method or source
|
|
||||||
pub struct UserId {
|
|
||||||
/// Main User ID. Generally an user name or similar. Locally unique
|
|
||||||
pub 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
|
|
||||||
pub subuid: Option<String>,
|
|
||||||
/// Realm this account originates.
|
|
||||||
///
|
|
||||||
/// The Realm is usually described by a domain name but local policy may dictate an unrelated
|
|
||||||
/// mapping
|
|
||||||
pub realm: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserId {
|
|
||||||
pub fn new(uid: String, subuid: Option<String>, realm: Option<String>) -> Self {
|
|
||||||
Self { uid, subuid, realm }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for UserId {
|
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
let flags = DatabaseFlags::empty();
|
||||||
let r = write!(f, "{}", self.uid);
|
let db = RawDB::create(&env, Some("user"), flags)?;
|
||||||
if let Some(ref s) = self.subuid {
|
Ok(Self::new(env, db))
|
||||||
write!(f, "+{}", s)?;
|
|
||||||
}
|
|
||||||
if let Some(ref l) = self.realm {
|
|
||||||
write!(f, "@{}", l)?;
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
pub fn get(&self, uid: &str) -> Result<Option<LMDBorrow<RoTransaction, Archived<User>>>> {
|
||||||
/// Data on an user to base decisions on
|
let txn = self.env.begin_ro_txn()?;
|
||||||
///
|
if let Some(state) = self.db.get(&txn, &uid.as_bytes())? {
|
||||||
/// This of course includes authorization data, i.e. that users set roles
|
let ptr = state.into();
|
||||||
pub struct UserData {
|
Ok(Some(unsafe { LMDBorrow::new(ptr, txn) }))
|
||||||
/// A Person has N ≥ 0 roles.
|
|
||||||
/// Persons are only ever given roles, not permissions directly
|
|
||||||
pub roles: Vec<RoleIdentifier>,
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub passwd: Option<String>,
|
|
||||||
|
|
||||||
/// Additional data storage
|
|
||||||
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
|
|
||||||
kv: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserData {
|
|
||||||
pub fn new(roles: Vec<RoleIdentifier>, priority: u64) -> Self {
|
|
||||||
Self { roles, priority, kv: HashMap::new(), passwd: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_zero(i: &u64) -> bool {
|
|
||||||
*i == 0
|
|
||||||
}
|
|
||||||
const fn default_priority() -> u64 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
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, 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 {
|
} else {
|
||||||
pw
|
Ok(None)
|
||||||
});
|
}
|
||||||
( uid.clone()
|
}
|
||||||
, User::new(UserId::new(uid, None, None), user_data)
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
|
pub fn put(&self, uid: &str, user: &User) -> Result<()> {
|
||||||
let mut flags = lmdb::DatabaseFlags::empty();
|
let mut txn = self.env.begin_rw_txn()?;
|
||||||
let db = env.create_db(Some("userdb"), flags)?;
|
let flags = WriteFlags::empty();
|
||||||
debug!(&log, "Opened user db successfully.");
|
self.db.put(&mut txn, &uid.as_bytes(), user, flags)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Internal::new(log, env, db))
|
pub fn get_all(&self) -> Result<Vec<(String, User)>> {
|
||||||
}
|
let txn = self.env.begin_ro_txn()?;
|
||||||
|
let mut cursor = self.db.open_ro_cursor(&txn)?;
|
||||||
|
let iter = cursor.iter_start();
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let mut deserializer = rkyv::Infallible;
|
||||||
|
for user in iter {
|
||||||
|
let (uid, user) = user?;
|
||||||
|
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
|
||||||
|
let user: User = user.deserialize(&mut deserializer).unwrap();
|
||||||
|
out.push((uid, user));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Internal {
|
|
||||||
log: Logger,
|
|
||||||
env: Arc<Environment>,
|
|
||||||
db: lmdb::Database,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Internal {
|
|
||||||
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
|
|
||||||
Self { log, env, db }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_user_txn<T: Transaction>(&self, txn: &T, uid: &str) -> Result<Option<User>> {
|
|
||||||
match txn.get(self.db, &uid.as_bytes()) {
|
|
||||||
Ok(bytes) => {
|
|
||||||
Ok(Some(flexbuffers::from_slice(bytes)?))
|
|
||||||
},
|
|
||||||
Err(lmdb::Error::NotFound) => Ok(None),
|
|
||||||
Err(e) => Err(e.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_user(&self, uid: &str) -> Result<Option<User>> {
|
|
||||||
let txn = self.env.begin_ro_txn()?;
|
|
||||||
self.get_user_txn(&txn, uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_user_txn(&self, txn: &mut RwTransaction, uid: &str, user: &User) -> Result<()> {
|
|
||||||
let bytes = flexbuffers::to_vec(user)?;
|
|
||||||
txn.put(self.db, &uid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn put_user(&self, uid: &str, user: &User) -> Result<()> {
|
|
||||||
let mut txn = self.env.begin_rw_txn()?;
|
|
||||||
self.put_user_txn(&mut txn, uid, user)?;
|
|
||||||
txn.commit()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_users(&self) -> Result<Vec<User>> {
|
|
||||||
let txn = self.env.begin_ro_txn()?;
|
|
||||||
Ok(self.list_users_txn(&txn)?.collect())
|
|
||||||
}
|
|
||||||
pub fn list_users_txn<T: Transaction>(&self, txn: &T) -> Result<impl Iterator<Item=User>> {
|
|
||||||
let mut cursor = txn.open_ro_cursor(self.db)?;
|
|
||||||
Ok(cursor.iter_start().map(|buf| {
|
|
||||||
let (_kbuf, vbuf) = buf.unwrap();
|
|
||||||
flexbuffers::from_slice(vbuf).unwrap()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn login(&self, uid: &str, password: &[u8]) -> Result<Option<User>> {
|
|
||||||
let txn = self.env.begin_ro_txn()?;
|
|
||||||
Ok(self.get_user_txn(&txn, uid)?
|
|
||||||
.filter(|user| {
|
|
||||||
user.data.passwd.is_some()
|
|
||||||
&& argon2::verify_encoded(user.data.passwd.as_ref().unwrap(), password).unwrap_or(false)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
13
src/error.rs
13
src/error.rs
@ -8,6 +8,7 @@ use rsasl::SaslError;
|
|||||||
use futures::task as futures_task;
|
use futures::task as futures_task;
|
||||||
|
|
||||||
use paho_mqtt::errors as mqtt;
|
use paho_mqtt::errors as mqtt;
|
||||||
|
use crate::db::DBError;
|
||||||
|
|
||||||
//FIXME use crate::network;
|
//FIXME use crate::network;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ pub enum Error {
|
|||||||
IO(io::Error),
|
IO(io::Error),
|
||||||
Boxed(Box<dyn std::error::Error>),
|
Boxed(Box<dyn std::error::Error>),
|
||||||
Capnp(capnp::Error),
|
Capnp(capnp::Error),
|
||||||
LMDB(lmdb::Error),
|
DB(DBError),
|
||||||
FuturesSpawn(futures_task::SpawnError),
|
FuturesSpawn(futures_task::SpawnError),
|
||||||
MQTT(mqtt::Error),
|
MQTT(mqtt::Error),
|
||||||
BadVersion((u32,u32)),
|
BadVersion((u32,u32)),
|
||||||
@ -43,8 +44,8 @@ impl fmt::Display for Error {
|
|||||||
Error::Capnp(e) => {
|
Error::Capnp(e) => {
|
||||||
write!(f, "Cap'n Proto Error: {}", e)
|
write!(f, "Cap'n Proto Error: {}", e)
|
||||||
},
|
},
|
||||||
Error::LMDB(e) => {
|
Error::DB(e) => {
|
||||||
write!(f, "LMDB Error: {}", e)
|
write!(f, "DB Error: {:?}", e)
|
||||||
},
|
},
|
||||||
Error::FuturesSpawn(e) => {
|
Error::FuturesSpawn(e) => {
|
||||||
write!(f, "Future could not be spawned: {}", e)
|
write!(f, "Future could not be spawned: {}", e)
|
||||||
@ -92,9 +93,9 @@ impl From<capnp::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<lmdb::Error> for Error {
|
impl From<DBError> for Error {
|
||||||
fn from(e: lmdb::Error) -> Error {
|
fn from(e: DBError) -> Error {
|
||||||
Error::LMDB(e)
|
Error::DB(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ mod space;
|
|||||||
mod resource;
|
mod resource;
|
||||||
mod schema;
|
mod schema;
|
||||||
mod state;
|
mod state;
|
||||||
mod db;
|
pub mod db;
|
||||||
mod network;
|
mod network;
|
||||||
pub mod oid;
|
pub mod oid;
|
||||||
mod varint;
|
mod varint;
|
||||||
|
@ -95,7 +95,11 @@ impl RoleIdentifier {
|
|||||||
|
|
||||||
impl fmt::Display for RoleIdentifier {
|
impl fmt::Display for RoleIdentifier {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}/{}", self.name, self.source)
|
if self.source != "" {
|
||||||
|
write!(f, "{}/{}", self.name, self.source)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +110,7 @@ impl std::str::FromStr for RoleIdentifier {
|
|||||||
if let Some((name, source)) = split_once(s, '/') {
|
if let Some((name, source)) = split_once(s, '/') {
|
||||||
Ok(RoleIdentifier { name: name.to_string(), source: source.to_string() })
|
Ok(RoleIdentifier { name: name.to_string(), source: source.to_string() })
|
||||||
} else {
|
} else {
|
||||||
Err(RoleFromStrError::Invalid)
|
Ok(RoleIdentifier { name: s.to_string(), source: String::new() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user