diff --git a/bffhd/capnp/machine.rs b/bffhd/capnp/machine.rs index 4e5bc7a..202baf0 100644 --- a/bffhd/capnp/machine.rs +++ b/bffhd/capnp/machine.rs @@ -1,15 +1,25 @@ -use api::machine_capnp::machine::{ - admin, admin::Server as AdminServer, - check, check::Server as CheckServer, - info, info::Server as InfoServer, - in_use as inuse, in_use::Server as InUseServer, - manage, manage::Server as ManageServer, - use_, use_::Server as UseServer, -}; +use crate::resources::modules::fabaccess::MachineState; +use crate::resources::Resource; use crate::session::SessionHandle; +use api::machine_capnp::machine::{ + admin, admin::Server as AdminServer, check, check::Server as CheckServer, in_use as inuse, + in_use::Server as InUseServer, info, info::Server as InfoServer, manage, + manage::Server as ManageServer, use_, use_::Server as UseServer, Builder, +}; +use capnp::capability::Promise; +use capnp_rpc::pry; +#[derive(Clone)] pub struct Machine { session: SessionHandle, + resource: Resource, +} + +impl Machine { + /// Builds a machine into the given builder. Re + pub fn build(session: SessionHandle, resource: Resource, builder: Builder) { + if resource.visible(&session) {} + } } impl InfoServer for Machine { @@ -17,8 +27,8 @@ impl InfoServer for Machine { &mut self, _: info::GetPropertyListParams, _: info::GetPropertyListResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -26,38 +36,46 @@ impl InfoServer for Machine { &mut self, _: info::GetReservationListParams, _: info::GetReservationListResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } } impl UseServer for Machine { - fn use_( - &mut self, - _: use_::UseParams, - _: use_::UseResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + fn use_(&mut self, _: use_::UseParams, _: use_::UseResults) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + let user = session.get_user(); + resource.try_update(session, MachineState::used(user)).await; + Ok(()) + }) } + fn reserve( &mut self, _: use_::ReserveParams, _: use_::ReserveResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + let user = session.get_user(); + resource + .try_update(session, MachineState::reserved(user)) + .await; + Ok(()) + }) } + fn reserveto( &mut self, _: use_::ReservetoParams, _: use_::ReservetoResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -68,17 +86,21 @@ impl InUseServer for Machine { &mut self, _: inuse::GiveBackParams, _: inuse::GiveBackResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + resource.give_back(session.clone()).await; + Ok(()) + }) } + fn send_raw_data( &mut self, _: inuse::SendRawDataParams, _: inuse::SendRawDataResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -89,17 +111,18 @@ impl CheckServer for Machine { &mut self, _: check::CheckParams, _: check::CheckResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } + fn reject( &mut self, _: check::RejectParams, _: check::RejectResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -110,8 +133,8 @@ impl ManageServer for Machine { &mut self, _: manage::GetMachineInfoExtendedParams, _: manage::GetMachineInfoExtendedResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -119,8 +142,8 @@ impl ManageServer for Machine { &mut self, _: manage::SetPropertyParams, _: manage::SetPropertyResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -128,74 +151,104 @@ impl ManageServer for Machine { &mut self, _: manage::RemovePropertyParams, _: manage::RemovePropertyResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } + fn force_use( &mut self, _: manage::ForceUseParams, _: manage::ForceUseResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + resource + .force_set(MachineState::used(session.get_user())) + .await; + Ok(()) + }) } + fn force_free( &mut self, _: manage::ForceFreeParams, _: manage::ForceFreeResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + resource.force_set(MachineState::free()).await; + Ok(()) + }) } fn force_transfer( &mut self, _: manage::ForceTransferParams, _: manage::ForceTransferResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } + fn block( &mut self, _: manage::BlockParams, _: manage::BlockResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let resource = self.resource.clone(); + let session = self.session.clone(); + Promise::from_future(async move { + resource + .force_set(MachineState::blocked(session.get_user())) + .await; + Ok(()) + }) } fn disabled( &mut self, _: manage::DisabledParams, _: manage::DisabledResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let mut resource = self.resource.clone(); + Promise::from_future(async move { + resource.force_set(MachineState::disabled()).await; + Ok(()) + }) } } impl AdminServer for Machine { fn force_set_state( &mut self, - _: admin::ForceSetStateParams, + params: admin::ForceSetStateParams, _: admin::ForceSetStateResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + use api::schema::machine_capnp::machine::MachineState as APIMState; + let user = self.session.get_user(); + let state = match pry!(pry!(params.get()).get_state()) { + APIMState::Free => MachineState::free(), + APIMState::Blocked => MachineState::blocked(user), + APIMState::Disabled => MachineState::disabled(), + APIMState::InUse => MachineState::used(user), + APIMState::Reserved => MachineState::reserved(user), + APIMState::ToCheck => MachineState::check(user), + APIMState::Totakeover => return Promise::err(::capnp::Error::unimplemented( + "totakeover not implemented".to_string(), + )), + }; + self.resource.force_set(state); + Promise::ok(()) } fn force_set_user( &mut self, _: admin::ForceSetUserParams, _: admin::ForceSetUserResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -203,8 +256,8 @@ impl AdminServer for Machine { &mut self, _: admin::GetAdminPropertyListParams, _: admin::GetAdminPropertyListResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -212,8 +265,8 @@ impl AdminServer for Machine { &mut self, _: admin::SetAdminPropertyParams, _: admin::SetAdminPropertyResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -221,8 +274,8 @@ impl AdminServer for Machine { &mut self, _: admin::RemoveAdminPropertyParams, _: admin::RemoveAdminPropertyResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } diff --git a/bffhd/capnp/machinesystem.rs b/bffhd/capnp/machinesystem.rs index 9af9b16..bbd12ca 100644 --- a/bffhd/capnp/machinesystem.rs +++ b/bffhd/capnp/machinesystem.rs @@ -4,23 +4,29 @@ use api::machinesystem_capnp::machine_system::{ info, InfoParams, InfoResults, Server as MachineSystem, }; use capnp::capability::Promise; +use capnp_rpc::pry; +use crate::capnp::machine::Machine; +use crate::resources::Resource; +use crate::resources::search::ResourcesHandle; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Machines { session: SessionHandle, + resources: ResourcesHandle, } impl Machines { pub fn new(session: SessionHandle) -> Self { - Self { session } + let resources = ResourcesHandle::new(); + Self { session, resources } } } impl MachineSystem for Machines { - fn info(&mut self, _: InfoParams, _: InfoResults) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + fn info(&mut self, _: InfoParams, mut result: InfoResults) -> Promise<(), ::capnp::Error> { + // TODO permission checking + result.get().set_info(capnp_rpc::new_client(self.clone())); + Promise::ok(()) } } @@ -28,28 +34,52 @@ impl info::Server for Machines { fn get_machine_list( &mut self, _: info::GetMachineListParams, - _: info::GetMachineListResults, + mut result: info::GetMachineListResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let machine_list: Vec<(usize, &Resource)> = self.resources.list_all() + .into_iter() + .filter(|resource| resource.visible(&self.session)) + .enumerate() + .collect(); + let mut builder = result.get().init_machine_list(machine_list.len() as u32); + for (i, m) in machine_list { + let resource = m.clone(); + let mut mbuilder = builder.reborrow().get(i as u32); + Machine::build(self.session.clone(), resource, mbuilder); + } + + Promise::ok(()) } + fn get_machine( &mut self, - _: info::GetMachineParams, - _: info::GetMachineResults, + params: info::GetMachineParams, + mut result: info::GetMachineResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let params = pry!(params.get()); + let id = pry!(params.get_id()); + + if let Some(resource) = self.resources.get_by_id(id) { + let mut builder = result.get(); + Machine::build(self.session.clone(), resource.clone(), builder); + } + + Promise::ok(()) } + fn get_machine_u_r_n( &mut self, - _: info::GetMachineURNParams, - _: info::GetMachineURNResults, + params: info::GetMachineURNParams, + mut result: info::GetMachineURNResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let params = pry!(params.get()); + let urn = pry!(params.get_urn()); + + if let Some(resource) = self.resources.get_by_urn(urn) { + let mut builder = result.get(); + Machine::build(self.session.clone(), resource.clone(), builder); + } + + Promise::ok(()) } } diff --git a/bffhd/db/mod.rs b/bffhd/db/mod.rs index 483e7e4..e8d2f66 100644 --- a/bffhd/db/mod.rs +++ b/bffhd/db/mod.rs @@ -43,7 +43,7 @@ use rkyv::Deserialize; use rkyv::ser::serializers::AlignedSerializer; use std::sync::Arc; use std::path::Path; -use crate::users::{User, UserDB}; +use crate::users::db::{User, UserDB}; use std::collections::HashMap; use crate::resources::state::{OwnedEntry, State, db::StateDB}; use std::iter::FromIterator; diff --git a/bffhd/initiators/mod.rs b/bffhd/initiators/mod.rs index ad62e0a..2ddbe08 100644 --- a/bffhd/initiators/mod.rs +++ b/bffhd/initiators/mod.rs @@ -5,7 +5,7 @@ use async_channel as channel; use async_oneshot as oneshot; use futures_signals::signal::Signal; use futures_util::future::BoxFuture; -use crate::resources::{Error, Update}; +use crate::resources::driver::{Error, Update}; use crate::resources::claim::{ResourceID, UserID}; use crate::resources::state::State; diff --git a/bffhd/lib.rs b/bffhd/lib.rs index cbb3b3a..3685876 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -87,6 +87,8 @@ impl Diflouroborane { SIGTERM, ]).context("Failed to construct signal handler")?; + + let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?; diff --git a/bffhd/resources/claim.rs b/bffhd/resources/claim.rs index 8044797..4fe699f 100644 --- a/bffhd/resources/claim.rs +++ b/bffhd/resources/claim.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use async_channel::Sender; use lmdb::Environment; -use crate::resources::Update; +use crate::resources::driver::Update; #[derive(Clone, Debug)] /// Database of currently valid claims, interests and notify, as far as applicable diff --git a/bffhd/resources/driver.rs b/bffhd/resources/driver.rs new file mode 100644 index 0000000..156f4f6 --- /dev/null +++ b/bffhd/resources/driver.rs @@ -0,0 +1,140 @@ +use std::fmt::Debug; +use async_trait::async_trait; + +use futures_signals::signal::Mutable; +use async_oneshot::Sender; +use async_channel::Receiver; + +use super::state::State; +use super::state::db::StateAccessor; + +/// A resources in BFFH has to contain several different parts; +/// - Currently set state +/// - Execution state of attached actors (⇒ BFFH's job) +/// - Output of interal logic of a resources +/// ⇒ Resource logic gets read access to set state and write access to output state. +/// ⇒ state `update` happens via resources logic. This logic should do access control. If the update +/// succeeds then BFFH stores those input parameters ("set" state) and results / output state. +/// Storing input parameters is relevant so that BFFH can know that an "update" is a no-op +/// without having to run the module code. +/// ⇒ in fact actors only really care about the output state, and shouldn't (need to) see "set" +/// state. +/// ⇒ example reserving: +/// - Claimant sends 'update' message with a new state +/// - Doesn't set the state until `update` has returned Ok. +/// - This runs the `update` function with that new state and the claimants user context returning +/// either an Ok or an Error. +/// - Error is returned to Claimant to show user, stop. +/// - On ok: +/// - Commit new "set" state, storing it and making it visible to all other claimants +/// - Commit new output state, storing it and notifying all connected actors / Notify +/// ⇒ BFFHs job in this whole ordeal is: +/// - Message passing primitives so that update message are queued +/// - As reliable as possible storage system for input and output state +/// - Again message passing so that updates are broadcasted to all Notify and Actors. +/// ⇒ Resource module's job is: +/// - Validating updates semantically i.e. are the types correct +/// - Check authorization of updates i.e. is this user allowed to do that +#[async_trait] +pub trait ResourceModel: Debug { + /// Run whatever internal logic this resources has for the given State update, and return the + /// new output state that this update produces. + async fn on_update(&mut self, input: &State) -> Result; + async fn shutdown(&mut self); +} + +#[derive(Debug)] +pub struct Passthrough; +#[async_trait] +impl ResourceModel for Passthrough { + async fn on_update(&mut self, input: &State) -> Result { + Ok(input.clone()) + } + + async fn shutdown(&mut self) {} +} + +/// Error type a resources implementation can produce +#[derive(Debug)] +pub enum Error { + Internal(Box), + Denied, +} + +// TODO: more message context +#[derive(Debug)] +pub struct Update { + pub state: State, + pub errchan: Sender, +} + +#[derive(Debug)] +pub struct ResourceDriver { + // putput + res: Box, + + // input + rx: Receiver, + + // output + db: StateAccessor, + + signal: Mutable, +} + +impl ResourceDriver { + pub async fn drive_to_end(&mut self) { + while let Ok(update) = self.rx.recv().await { + let state = update.state; + let mut errchan = update.errchan; + + match self.res.on_update(&state).await { + Ok(outstate) => { + // FIXME: Send any error here to some global error collector. A failed write to + // the DB is not necessarily fatal, but it means that BFFH is now in an + // inconsistent state until a future update succeeds with writing to the DB. + // Not applying the new state isn't correct either since we don't know what the + // internal logic of the resources has done to make this happen. + // Another half right solution is to unwrap and recreate everything. + // "Best" solution would be to tell the resources to rollback their interal + // changes on a fatal failure and then notify the Claimant, while simply trying + // again for temporary failures. + let _ = self.db.set(&state, &outstate); + self.signal.set(outstate); + }, + Err(e) => { + let _ = errchan.send(e); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::pin::Pin; + use std::task::Poll; + use std::future::Future; + use super::*; + + #[futures_test::test] + async fn test_passthrough_is_id() { + let inp = state::tests::gen_random(); + + let mut res = Passthrough; + let out = res.on_update(&inp).await.unwrap(); + assert_eq!(inp, out); + } + + #[test] + fn test_passthrough_is_always_ready() { + let inp = State::build().finish(); + + let mut res = Passthrough; + let mut cx = futures_test::task::panic_context(); + if let Poll::Ready(_) = Pin::new(&mut res.on_update(&inp)).poll(&mut cx) { + return; + } + panic!("Passthrough returned Poll::Pending") + } +} diff --git a/bffhd/resources/mod.rs b/bffhd/resources/mod.rs index 3dbd15c..4b0653d 100644 --- a/bffhd/resources/mod.rs +++ b/bffhd/resources/mod.rs @@ -1,145 +1,113 @@ -use std::fmt::Debug; -use async_trait::async_trait; +use crate::resources::modules::fabaccess::{MachineState, Status}; +use crate::resources::state::State; +use crate::session::SessionHandle; +use crate::users::User; -use futures_signals::signal::Mutable; -use async_oneshot::Sender; -use async_channel::Receiver; - -use state::State; -use state::db::StateAccessor; - -pub mod state; pub mod claim; pub mod db; +pub mod driver; +pub mod search; +pub mod state; +pub mod modules; -/// A resources in BFFH has to contain several different parts; -/// - Currently set state -/// - Execution state of attached actors (⇒ BFFH's job) -/// - Output of interal logic of a resources -/// ⇒ Resource logic gets read access to set state and write access to output state. -/// ⇒ state `update` happens via resources logic. This logic should do access control. If the update -/// succeeds then BFFH stores those input parameters ("set" state) and results / output state. -/// Storing input parameters is relevant so that BFFH can know that an "update" is a no-op -/// without having to run the module code. -/// ⇒ in fact actors only really care about the output state, and shouldn't (need to) see "set" -/// state. -/// ⇒ example reserving: -/// - Claimant sends 'update' message with a new state -/// - Doesn't set the state until `update` has returned Ok. -/// - This runs the `update` function with that new state and the claimants user context returning -/// either an Ok or an Error. -/// - Error is returned to Claimant to show user, stop. -/// - On ok: -/// - Commit new "set" state, storing it and making it visible to all other claimants -/// - Commit new output state, storing it and notifying all connected actors / Notify -/// ⇒ BFFHs job in this whole ordeal is: -/// - Message passing primitives so that update message are queued -/// - As reliable as possible storage system for input and output state -/// - Again message passing so that updates are broadcasted to all Notify and Actors. -/// ⇒ Resource module's job is: -/// - Validating updates semantically i.e. are the types correct -/// - Check authorization of updates i.e. is this user allowed to do that -#[async_trait] -pub trait ResourceModel: Debug { - /// Run whatever internal logic this resources has for the given State update, and return the - /// new output state that this update produces. - async fn on_update(&mut self, input: &State) -> Result; - async fn shutdown(&mut self); -} +pub struct PermissionDenied; -#[derive(Debug)] -pub struct Passthrough; -#[async_trait] -impl ResourceModel for Passthrough { - async fn on_update(&mut self, input: &State) -> Result { - Ok(input.clone()) +#[derive(Clone)] +pub struct Resource {} + +impl Resource { + pub fn get_state(&self) -> MachineState { + unimplemented!() } - async fn shutdown(&mut self) {} -} + fn set_state(&self, state: MachineState) { + unimplemented!() + } -/// Error type a resources implementation can produce -#[derive(Debug)] -pub enum Error { - Internal(Box), - Denied, -} + fn set_previous_user(&self, user: User) { + unimplemented!() + } -// TODO: more message context -#[derive(Debug)] -pub struct Update { - pub state: State, - pub errchan: Sender, -} + pub async fn try_update(&self, session: SessionHandle, new: MachineState) { + let old = self.get_state(); + let user = session.get_user(); -#[derive(Debug)] -pub struct ResourceDriver { - // putput - res: Box, + if session.has_manage(self) // Default allow for managers - // input - rx: Receiver, + || (session.has_write(self) // Decision tree for writers + && match (old.state, &new.state) { + // Going from available to used by the person requesting is okay. + (Status::Free, Status::InUse(who)) + // Check that the person requesting does not request for somebody else. + // *That* is manage privilege. + if who == &user => true, - // output - db: StateAccessor, + // Reserving things for ourself is okay. + (Status::Free, Status::Reserved(whom)) + if &user == whom => true, - signal: Mutable, -} + // Returning things we've been using is okay. This includes both if + // they're being freed or marked as to be checked. + (Status::InUse(who), Status::Free | Status::ToCheck(_)) + if who == user => true, -impl ResourceDriver { - pub async fn drive_to_end(&mut self) { - while let Ok(update) = self.rx.recv().await { - let state = update.state; - let mut errchan = update.errchan; + // Un-reserving things we reserved is okay + (Status::Reserved(whom), Status::Free) + if user == whom => true, + // Using things that we've reserved is okay. But the person requesting + // that has to be the person that reserved the machine. Otherwise + // somebody could make a machine reserved by a different user as used by + // that different user but use it themself. + (Status::Reserved(whom), Status::InUse(who)) + if user == whom && who == &whom => true, - match self.res.on_update(&state).await { - Ok(outstate) => { - // FIXME: Send any error here to some global error collector. A failed write to - // the DB is not necessarily fatal, but it means that BFFH is now in an - // inconsistent state until a future update succeeds with writing to the DB. - // Not applying the new state isn't correct either since we don't know what the - // internal logic of the resources has done to make this happen. - // Another half right solution is to unwrap and recreate everything. - // "Best" solution would be to tell the resources to rollback their interal - // changes on a fatal failure and then notify the Claimant, while simply trying - // again for temporary failures. - let _ = self.db.set(&state, &outstate); - self.signal.set(outstate); - }, - Err(e) => { - let _ = errchan.send(e); - } + // Default is deny. + _ => false + }) + + // Default permissions everybody has + || match (old.state, &new.state) { + // Returning things we've been using is okay. This includes both if + // they're being freed or marked as to be checked. + (Status::InUse(who), Status::Free | Status::ToCheck(_)) if who == user => true, + + // Un-reserving things we reserved is okay + (Status::Reserved(whom), Status::Free) if user == whom => true, + + // Default is deny. + _ => false, + } + { + self.set_state(new); + } + } + + pub async fn give_back(&self, session: SessionHandle) { + if let Status::InUse(user) = self.get_state().state { + if user == session.get_user() { + self.set_state(MachineState::free()); + self.set_previous_user(user); } } } -} -#[cfg(test)] -mod tests { - use std::pin::Pin; - use std::task::Poll; - use std::future::Future; - use super::*; - - #[futures_test::test] - async fn test_passthrough_is_id() { - let inp = state::tests::gen_random(); - - let mut res = Passthrough; - let out = res.on_update(&inp).await.unwrap(); - assert_eq!(inp, out); + pub async fn force_set(&self, new: MachineState) { + unimplemented!() } - #[test] - fn test_passthrough_is_always_ready() { - let inp = State::build().finish(); + pub fn visible(&self, session: &SessionHandle) -> bool { + session.has_disclose(self) || self.is_owned_by(session.get_user()) + } - let mut res = Passthrough; - let mut cx = futures_test::task::panic_context(); - if let Poll::Ready(_) = Pin::new(&mut res.on_update(&inp)).poll(&mut cx) { - return; + pub fn is_owned_by(&self, owner: User) -> bool { + match self.get_state().state { + Status::Free | Status::Disabled => false, + + Status::InUse(user) + | Status::ToCheck(user) + | Status::Blocked(user) + | Status::Reserved(user) => user == owner, } - panic!("Passthrough returned Poll::Pending") } -} \ No newline at end of file +} diff --git a/bffhd/resources/modules/fabaccess.rs b/bffhd/resources/modules/fabaccess.rs new file mode 100644 index 0000000..3d6ccd0 --- /dev/null +++ b/bffhd/resources/modules/fabaccess.rs @@ -0,0 +1,114 @@ +use crate::utils::oid::ObjectIdentifier; +use once_cell::sync::Lazy; +use rkyv::{Archive, Deserialize, Serialize}; +use rkyv_dyn::{DynError, DynSerializer}; +use std::str::FromStr; + +use crate::oidvalue; +use crate::session::SessionHandle; +use crate::users::User; + +/// Status of a Machine +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Archive, + rkyv::Serialize, + rkyv::Deserialize, + serde::Serialize, + serde::Deserialize, +)] +#[archive_attr(derive(Debug, PartialEq, serde::Serialize, serde::Deserialize))] +pub enum Status { + /// Not currently used by anybody + Free, + /// Used by somebody + InUse(User), + /// Was used by somebody and now needs to be checked for cleanliness + ToCheck(User), + /// Not used by anybody but also can not be used. E.g. down for maintenance + Blocked(User), + /// Disabled for some other reason + Disabled, + /// Reserved + Reserved(User), +} + +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Archive, + rkyv::Serialize, + rkyv::Deserialize, + serde::Serialize, + serde::Deserialize, +)] +#[archive_attr(derive(Debug, PartialEq, serde::Serialize, serde::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(user: User) -> Self { + Self { + state: Status::InUse(user), + } + } + + pub fn blocked(user: User) -> Self { + Self { + state: Status::Blocked(user), + } + } + + pub fn disabled() -> Self { + Self { + state: Status::Disabled, + } + } + + pub fn reserved(user: User) -> Self { + Self { + state: Status::Reserved(user), + } + } + + pub fn check(user: User) -> Self { + Self { + state: Status::ToCheck(user), + } + } + + pub fn make_used(&mut self, session: SessionHandle) -> Self { + unimplemented!() + } +} + +static OID_TYPE: Lazy = + Lazy::new(|| ObjectIdentifier::from_str("1.3.6.1.4.1.48398.612.1.14").unwrap()); +static OID_VALUE: Lazy = + Lazy::new(|| ObjectIdentifier::from_str("1.3.6.1.4.1.48398.612.2.4").unwrap()); +oidvalue!(OID_TYPE, MachineState, ArchivedMachineState); diff --git a/bffhd/resources/modules/mod.rs b/bffhd/resources/modules/mod.rs new file mode 100644 index 0000000..4262264 --- /dev/null +++ b/bffhd/resources/modules/mod.rs @@ -0,0 +1,2 @@ + +pub mod fabaccess; \ No newline at end of file diff --git a/bffhd/resources/search.rs b/bffhd/resources/search.rs new file mode 100644 index 0000000..a1dcb5c --- /dev/null +++ b/bffhd/resources/search.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; +use crate::resources::Resource; + +struct Inner { + +} + +impl Inner { + pub fn new() -> Self { + Self { } + } +} + +#[derive(Clone)] +pub struct ResourcesHandle { + inner: Arc, +} + +impl ResourcesHandle { + pub fn new() -> Self { + Self { + inner: Arc::new(Inner::new()), + } + } + + pub fn list_all(&self) -> impl IntoIterator { + unimplemented!(); + &[] + } + + pub fn get_by_id(&self, id: &str) -> Option<&Resource> { + unimplemented!() + } + + pub fn get_by_urn(&self, urn: &str) -> Option<&Resource> { + unimplemented!() + } +} diff --git a/bffhd/resources/state/value.rs b/bffhd/resources/state/value.rs index d304f35..91dfadf 100644 --- a/bffhd/resources/state/value.rs +++ b/bffhd/resources/state/value.rs @@ -1,24 +1,21 @@ -use std::{ - fmt, - ptr, - any::Any, - hash::Hash, - str::FromStr, -}; +use std::{any::Any, fmt, hash::Hash, ptr, str::FromStr}; -use rkyv::{Archive, Archived, Serialize, Deserialize, out_field, Fallible, DeserializeUnsized, ArchivePointee, ArchiveUnsized, ArchivedMetadata, SerializeUnsized, }; -use rkyv_dyn::{DynSerializer, DynError, DynDeserializer}; -use rkyv_typename::TypeName; use ptr_meta::{DynMetadata, Pointee}; +use rkyv::{ + out_field, Archive, ArchivePointee, ArchiveUnsized, Archived, ArchivedMetadata, Deserialize, + DeserializeUnsized, Fallible, Serialize, SerializeUnsized, +}; +use rkyv_dyn::{DynDeserializer, DynError, DynSerializer}; +use rkyv_typename::TypeName; -use inventory; use crate::utils::oid::ObjectIdentifier; -use rkyv::ser::{Serializer, ScratchSpace}; -use std::collections::HashMap; -use std::alloc::Layout; -use serde::ser::SerializeMap; -use std::fmt::Formatter; +use inventory; +use rkyv::ser::{ScratchSpace, Serializer}; use serde::de::Error as SerdeError; +use serde::ser::SerializeMap; +use std::alloc::Layout; +use std::collections::HashMap; +use std::fmt::Formatter; use std::mem::MaybeUninit; /// Adding a custom type to BFFH state management: @@ -28,7 +25,7 @@ use std::mem::MaybeUninit; /// 2. Implement rkyv's [`Serialize`](rkyv::Serialize). /// 3. Implement TypeOid on your Archived type (i.e. `::Archived`) /// 4. Implement this -pub trait Value: Any + fmt::Debug + erased_serde::Serialize + Sync { +pub trait Value: Any + fmt::Debug + erased_serde::Serialize + Sync { /// Initialize `&mut self` from `deserializer` /// /// At the point this is called &mut self is of undefined value but guaranteed to be well @@ -37,8 +34,10 @@ pub trait Value: Any + fmt::Debug + erased_serde::Serialize + Sync { /// To this end you *must* initialize `self` **completely**. Serde will do the right thing if /// you directly deserialize the type you're implementing `Value` for, but for manual /// implementations this is important to keep in mind. - fn deserialize_init<'de>(&mut self, deserializer: &mut dyn erased_serde::Deserializer<'de>) - -> Result<(), erased_serde::Error>; + fn deserialize_init<'de>( + &mut self, + deserializer: &mut dyn erased_serde::Deserializer<'de>, + ) -> Result<(), erased_serde::Error>; /// Implement `PartialEq` dynamically. /// @@ -55,19 +54,27 @@ erased_serde::serialize_trait_object!(SerializeValue); erased_serde::serialize_trait_object!(DeserializeValue); impl Value for T - where T: Any + fmt::Debug + PartialEq + Sync - + erased_serde::Serialize - + for<'de> serde::Deserialize<'de> +where + T: Any + + fmt::Debug + + PartialEq + + Sync + + erased_serde::Serialize + + for<'de> serde::Deserialize<'de>, { - fn deserialize_init<'de>(&mut self, deserializer: &mut dyn erased_serde::Deserializer<'de>) - -> Result<(), erased_serde::Error> - { + fn deserialize_init<'de>( + &mut self, + deserializer: &mut dyn erased_serde::Deserializer<'de>, + ) -> Result<(), erased_serde::Error> { *self = erased_serde::deserialize(deserializer)?; Ok(()) } fn dyn_eq(&self, other: &dyn Value) -> bool { - other.as_any().downcast_ref().map_or(false, |other: &T| other == self) + other + .as_any() + .downcast_ref() + .map_or(false, |other: &T| other == self) } fn as_value(&self) -> &dyn Value { @@ -89,7 +96,8 @@ impl PartialEq for dyn Value { pub(super) struct DynVal<'a>(pub &'a dyn SerializeValue); impl<'a> serde::Serialize for DynVal<'a> { fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer + where + S: serde::Serializer, { let mut ser = serializer.serialize_map(Some(1))?; let oid = self.0.archived_type_oid(); @@ -101,7 +109,8 @@ impl<'a> serde::Serialize for DynVal<'a> { pub(super) struct DynOwnedVal(pub Box); impl<'de> serde::Deserialize<'de> for DynOwnedVal { fn deserialize(deserializer: D) -> Result - where D: serde::Deserializer<'de> + where + D: serde::Deserializer<'de>, { deserializer.deserialize_map(DynValVisitor) } @@ -116,22 +125,21 @@ impl<'de> serde::de::Visitor<'de> for DynValVisitor { write!(formatter, "an one entry map from OID to some value object") } - fn visit_map>(self, mut map: A) -> Result - { + fn visit_map>(self, mut map: A) -> Result { // Bad magic code. Problem we have to solve: We only know how to parse whatever comes // after the OID after having looked at the OID. We have zero static type info available // during deserialization. So: // Get OID first. That's easy, we know it's the key, we know how to read it. - let oid: ObjectIdentifier = map.next_key()? - .ok_or(A::Error::missing_field("oid"))?; + let oid: ObjectIdentifier = map.next_key()?.ok_or(A::Error::missing_field("oid"))?; // Get the Value vtable for that OID. Or fail because we don't know that OID, either works. - let valimpl = IMPL_REGISTRY.get(ImplId::from_type_oid(&oid)) - .ok_or(serde::de::Error::invalid_value( + let valimpl = IMPL_REGISTRY.get(ImplId::from_type_oid(&oid)).ok_or( + serde::de::Error::invalid_value( serde::de::Unexpected::Other("unknown oid"), &"oid an implementation was registered for", - ))?; + ), + )?; // Casting random usize you find on the side of the road as vtable on unchecked pointers. // What could possibly go wrong? >:D @@ -151,9 +159,7 @@ impl<'de> serde::de::Visitor<'de> for DynValVisitor { // validate in any other way if this is sane? // Well... let ptr: *mut () = std::alloc::alloc(layout).cast::<()>(); - let b = Box::from_raw(ptr_meta::from_raw_parts_mut( - ptr, - meta)); + let b = Box::from_raw(ptr_meta::from_raw_parts_mut(ptr, meta)); // We make this a MaybeUninit so `Drop` is never called on the uninitialized value MaybeUninit::new(b) @@ -173,7 +179,8 @@ impl<'de> serde::de::DeserializeSeed<'de> for InitIntoSelf { type Value = Box; fn deserialize(mut self, deserializer: D) -> Result - where D: serde::Deserializer<'de> + where + D: serde::Deserializer<'de>, { let mut deser = ::erase(deserializer); @@ -182,8 +189,9 @@ impl<'de> serde::de::DeserializeSeed<'de> for InitIntoSelf { let selfptr = unsafe { &mut *self.0.as_mut_ptr() }; // Hey, better initialize late than never. - selfptr.deserialize_init(&mut deser).map_err(|e| - D::Error::custom(e))?; + selfptr + .deserialize_init(&mut deser) + .map_err(|e| D::Error::custom(e))?; // Assuming `deserialize_init` didn't error and did its job this is now safe. unsafe { Ok(self.0.assume_init()) } @@ -208,7 +216,6 @@ impl SerializeUnsized for dyn Serializ } } - /// Serialize dynamic types by storing an OID alongside pub trait SerializeDynOid { fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result; @@ -216,8 +223,9 @@ pub trait SerializeDynOid { } impl SerializeDynOid for T - where T: for<'a> Serialize, - T::Archived: TypeOid, +where + T: for<'a> Serialize, + T::Archived: TypeOid, { fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result { serializer.serialize_value(self) @@ -247,8 +255,8 @@ pub trait SerializeValue: Value + SerializeDynOid { } impl SerializeValue for T - where - T::Archived: RegisteredImpl +where + T::Archived: RegisteredImpl, { fn dyn_clone(&self) -> Box { Box::new(self.clone()) @@ -278,17 +286,21 @@ impl ArchivePointee for dyn DeserializeValue { } } impl DeserializeUnsized for dyn DeserializeValue { - unsafe fn deserialize_unsized(&self, - mut deserializer: &mut D, - mut alloc: impl FnMut(Layout) -> *mut u8 + unsafe fn deserialize_unsized( + &self, + mut deserializer: &mut D, + mut alloc: impl FnMut(Layout) -> *mut u8, ) -> Result<*mut (), D::Error> { - self.deserialize_dynoid(&mut deserializer, &mut alloc).map_err(|e| *e.downcast().unwrap()) + self.deserialize_dynoid(&mut deserializer, &mut alloc) + .map_err(|e| *e.downcast().unwrap()) } - fn deserialize_metadata(&self, mut deserializer: &mut D) - -> Result<::Metadata, D::Error> - { - self.deserialize_dynoid_metadata(&mut deserializer).map_err(|e| *e.downcast().unwrap()) + fn deserialize_metadata( + &self, + mut deserializer: &mut D, + ) -> Result<::Metadata, D::Error> { + self.deserialize_dynoid_metadata(&mut deserializer) + .map_err(|e| *e.downcast().unwrap()) } } @@ -296,7 +308,12 @@ impl ArchiveUnsized for dyn SerializeValue { type Archived = dyn DeserializeValue; type MetadataResolver = ::Resolver; - unsafe fn resolve_metadata(&self, pos: usize, resolver: Self::MetadataResolver, out: *mut ArchivedMetadata) { + unsafe fn resolve_metadata( + &self, + pos: usize, + resolver: Self::MetadataResolver, + out: *mut ArchivedMetadata, + ) { let (oid_pos, oid) = out_field!(out.type_oid); let type_oid = self.archived_type_oid(); type_oid.resolve(pos + oid_pos, resolver, oid); @@ -316,10 +333,14 @@ impl ArchivedValueMetadata { pub fn vtable(&self) -> usize { IMPL_REGISTRY - .get(ImplId::from_type_oid(&self.type_oid)).expect(&format!("Unregistered \ + .get(ImplId::from_type_oid(&self.type_oid)) + .expect(&format!( + "Unregistered \ type \ oid \ - {:?}", self.type_oid)) + {:?}", + self.type_oid + )) .vtable } @@ -342,7 +363,7 @@ impl<'a> ImplId<'a> { impl ImplId<'static> { fn new() -> Self { Self { - type_oid: &T::type_oid() + type_oid: &T::type_oid(), } } } @@ -362,15 +383,6 @@ pub struct ImplDebugInfo { pub line: u32, pub column: u32, } -macro_rules! debug_info { - () => { - ImplDebugInfo { - file: core::file!(), - line: core::line!(), - column: core::column!(), - } - } -} impl ImplData<'_> { pub unsafe fn pointer_metadata(&self) -> DynMetadata { @@ -407,7 +419,9 @@ struct ImplRegistry { impl ImplRegistry { fn new() -> Self { - Self { oid_to_data: HashMap::new() } + Self { + oid_to_data: HashMap::new(), + } } fn add_entry(&mut self, entry: &'static ImplEntry) { @@ -415,10 +429,14 @@ impl ImplRegistry { if let Some(old) = old_val { eprintln!("Value impl oid conflict for {:?}", entry.id.type_oid); - eprintln!("Existing impl registered at {}:{}:{}", - old.info.file, old.info.line, old.info.column); - eprintln!("New impl registered at {}:{}:{}", - entry.data.info.file, entry.data.info.line, entry.data.info.column); + eprintln!( + "Existing impl registered at {}:{}:{}", + old.info.file, old.info.line, old.info.column + ); + eprintln!( + "New impl registered at {}:{}:{}", + entry.data.info.file, entry.data.info.line, entry.data.info.column + ); } assert!(old_val.is_none()); } @@ -444,64 +462,89 @@ pub unsafe trait RegisteredImpl { fn debug_info() -> ImplDebugInfo; } -macro_rules! oiddeser { - ( $y:ty, $z:ty ) => { - impl DeserializeDynOid for $y - where $y: for<'a> Deserialize<$z, (dyn DynDeserializer + 'a)> - { - unsafe fn deserialize_dynoid(&self, deserializer: &mut dyn DynDeserializer, alloc: &mut dyn FnMut(Layout) -> *mut u8) -> Result<*mut (), DynError> { - let ptr = alloc(Layout::new::<$z>()).cast::<$z>(); - ptr.write(self.deserialize(deserializer)?); - Ok(ptr as *mut ()) +#[macro_use] +pub mod macros { + #[macro_export] + macro_rules! debug_info { + () => { + $crate::resources::state::value::ImplDebugInfo { + file: ::core::file!(), + line: ::core::line!(), + column: ::core::column!(), } + }; + } + #[macro_export] + macro_rules! oiddeser { + ( $y:ty, $z:ty ) => { + impl $crate::resources::state::value::DeserializeDynOid for $y + where + $y: for<'a> ::rkyv::Deserialize<$z, (dyn ::rkyv_dyn::DynDeserializer + 'a)>, + { + unsafe fn deserialize_dynoid( + &self, + deserializer: &mut dyn ::rkyv_dyn::DynDeserializer, + alloc: &mut dyn FnMut(::core::alloc::Layout) -> *mut u8, + ) -> Result<*mut (), ::rkyv_dyn::DynError> { + let ptr = alloc(::core::alloc::Layout::new::<$z>()).cast::<$z>(); + ptr.write(self.deserialize(deserializer)?); + Ok(ptr as *mut ()) + } - fn deserialize_dynoid_metadata(&self, _: &mut dyn DynDeserializer) -> Result<::Metadata, DynError> { - unsafe { - Ok(core::mem::transmute(ptr_meta::metadata( - core::ptr::null::<$z>() as *const dyn SerializeValue - ))) + fn deserialize_dynoid_metadata( + &self, + _: &mut dyn ::rkyv_dyn::DynDeserializer, + ) -> ::std::result::Result<::Metadata, ::rkyv_dyn::DynError> { + unsafe { + Ok(core::mem::transmute(ptr_meta::metadata( + ::core::ptr::null::<$z>() as *const dyn $crate::resources::state::value::SerializeValue, + ))) + } } } - } + }; } -} -macro_rules! oidvalue { - ( $x:ident, $y:ty ) => { - oidvalue! {$x, $y, $y} - }; - ( $x:ident, $y:ty, $z:ty ) => { - oiddeser! {$z, $y} + #[macro_export] + macro_rules! oidvalue { + ( $x:ident, $y:ty ) => { + $crate::oidvalue! {$x, $y, $y} + }; + ( $x:ident, $y:ty, $z:ty ) => { + $crate::oiddeser! {$z, $y} - impl TypeOid for $z { - fn type_oid() -> &'static ObjectIdentifier { - &$x - } + impl $crate::resources::state::value::TypeOid for $z { + fn type_oid() -> &'static $crate::utils::oid::ObjectIdentifier { + &$x + } - fn type_name() -> &'static str { - stringify!($y) - } + fn type_name() -> &'static str { + stringify!($y) + } - fn type_desc() -> &'static str { - "builtin" - } - } - unsafe impl RegisteredImpl for $z { - fn vtable() -> usize { - unsafe { - core::mem::transmute(ptr_meta::metadata( - core::ptr::null::<$z>() as *const dyn DeserializeValue - )) + fn type_desc() -> &'static str { + "builtin" } } - fn debug_info() -> ImplDebugInfo { - debug_info!() + unsafe impl $crate::resources::state::value::RegisteredImpl for $z { + fn vtable() -> usize { + unsafe { + ::core::mem::transmute(ptr_meta::metadata( + ::core::ptr::null::<$z>() as *const dyn $crate::resources::state::value::DeserializeValue + )) + } + } + fn debug_info() -> $crate::resources::state::value::ImplDebugInfo { + $crate::debug_info!() + } } - } - inventory::submit! {ImplEntry::new::<$z>()} + ::inventory::submit! {$crate::resources::state::value::ImplEntry::new::<$z>()} + }; } } +use macros::*; lazy_static::lazy_static! { pub static ref OID_BOOL: ObjectIdentifier = { @@ -563,9 +606,20 @@ oidvalue!(OID_I32, i32); oidvalue!(OID_I64, i64); oidvalue!(OID_I128, i128); -#[derive(serde::Serialize, serde::Deserialize)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] -#[archive_attr(derive(TypeName, Debug, PartialEq, serde::Serialize, serde::Deserialize))] +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Copy, + Clone, + PartialEq, + Eq, + Hash, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +#[archive_attr(derive(Debug, PartialEq, serde::Serialize, serde::Deserialize))] pub struct Vec3u8 { pub a: u8, pub b: u8, @@ -576,9 +630,9 @@ oidvalue!(OID_VEC3U8, Vec3u8, ArchivedVec3u8); #[cfg(test)] mod tests { use super::*; - use rand::Rng; use rand::distributions::Standard; use rand::prelude::Distribution; + use rand::Rng; impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Vec3u8 { @@ -588,4 +642,4 @@ mod tests { Vec3u8 { a, b, c } } } -} \ No newline at end of file +} diff --git a/bffhd/session/mod.rs b/bffhd/session/mod.rs index 0798d35..b1a0c3a 100644 --- a/bffhd/session/mod.rs +++ b/bffhd/session/mod.rs @@ -1,4 +1,7 @@ use std::sync::Arc; +use crate::authorization::roles::Role; +use crate::resources::Resource; +use crate::users::User; struct Inner { @@ -29,4 +32,20 @@ pub struct SessionHandle { } impl SessionHandle { + pub fn get_user(&self) -> User { + unimplemented!() + } + + pub fn has_disclose(&self, resource: &Resource) -> bool { + unimplemented!() + } + pub fn has_read(&self, resource: &Resource) -> bool { + unimplemented!() + } + pub fn has_write(&self, resource: &Resource) -> bool { + unimplemented!() + } + pub fn has_manage(&self, resource: &Resource) -> bool { + unimplemented!() + } } \ No newline at end of file diff --git a/bffhd/users/db.rs b/bffhd/users/db.rs index f28e275..a2e51be 100644 --- a/bffhd/users/db.rs +++ b/bffhd/users/db.rs @@ -1,11 +1,27 @@ +use crate::db::{AllocAdapter, Environment, RawDB, Result, DB}; +use crate::db::{DatabaseFlags, LMDBorrow, RoTransaction, WriteFlags}; +use lmdb::{RwTransaction, Transaction}; use std::collections::HashSet; use std::sync::Arc; -use lmdb::{RwTransaction, Transaction}; -use crate::db::{RawDB, DB, AllocAdapter, Environment, Result}; -use crate::db::{DatabaseFlags, LMDBorrow, RoTransaction, WriteFlags, }; -use super::User; -use rkyv::{Deserialize, Archived}; +use rkyv::{Archived, Deserialize}; + +#[derive( + Clone, + PartialEq, + Eq, + Debug, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, + serde::Serialize, + serde::Deserialize, +)] +pub struct User { + id: u128, + username: String, + roles: Vec, +} type Adapter = AllocAdapter; #[derive(Clone, Debug)] @@ -64,59 +80,3 @@ impl UserDB { Ok(out) } } - -pub struct UserIndex { - env: Arc, - usernames: RawDB, - roles: RawDB, -} - -impl UserIndex { - pub fn update(&self, old: &User, new: &User) -> Result<()> { - assert_eq!(old.id, new.id); - let mut txn = self.env.begin_rw_txn()?; - if old.username != new.username { - self.update_username(&mut txn, new.id, &old.username, &new.username)?; - } - - let mut to_remove: HashSet<&String> = old.roles.iter().collect(); - let mut to_add: HashSet<&String> = HashSet::new(); - for role in new.roles.iter() { - // If a role wasn't found in the old ones it's a new one that's being added - if !to_remove.remove(role) { - to_add.insert(role); - } - // Otherwise it's in both sets so we just ignore it. - } - - self.update_roles(&mut txn, new.id, to_remove, to_add)?; - txn.commit()?; - Ok(()) - } - - fn update_username(&self, txn: &mut RwTransaction, uid: u128, old: &String, new: &String) - -> Result<()> - { - let flags = WriteFlags::empty(); - self.usernames.del(txn, &old.as_bytes(), Some(&uid.to_ne_bytes()))?; - self.usernames.put(txn, &new.as_bytes(), &uid.to_ne_bytes(), flags)?; - Ok(()) - } - - fn update_roles(&self, - txn: &mut RwTransaction, - uid: u128, - remove: HashSet<&String>, - add: HashSet<&String> - ) -> Result<()> - { - let flags = WriteFlags::empty(); - for role in remove.iter() { - self.roles.del(txn, &role.as_bytes(), Some(&uid.to_ne_bytes()))?; - } - for role in add.iter() { - self.roles.put(txn, &role.as_bytes(), &uid.to_ne_bytes(), flags)?; - } - Ok(()) - } -} \ No newline at end of file diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index 455c25d..5616ddc 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -14,24 +14,43 @@ * along with this program. If not, see . */ +use rkyv::{Archive, Deserialize, Infallible, Serialize}; use std::ops::Deref; use std::sync::Arc; -use rkyv::{Archive, Serialize, Deserialize, Infallible}; -mod db; +pub mod db; -pub use db::UserDB; pub use crate::authentication::db::PassDB; +use crate::authorization::roles::Role; -#[derive(Debug, Clone, Archive, Serialize, Deserialize, serde::Serialize, serde::Deserialize)] +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Archive, + Serialize, + Deserialize, + serde::Serialize, + serde::Deserialize, +)] +#[archive_attr(derive(Debug, PartialEq, serde::Serialize, serde::Deserialize))] pub struct User { - id: u128, - username: String, - roles: Vec, + id: u64 } impl User { - pub fn new(id: u128, username: String, roles: Vec) -> Self { - User { id, username, roles } + pub fn new(id: u64) -> Self { + User { id } } -} \ No newline at end of file + + pub fn get_username(&self) -> &str { + unimplemented!() + } + + pub fn get_roles(&self) -> impl IntoIterator { + unimplemented!(); + [] + } +}