Api framework impl

This commit is contained in:
Nadja Reitzenstein 2022-03-13 17:29:21 +01:00
parent 999463e0e9
commit df5ee9a0a1
15 changed files with 807 additions and 408 deletions

View File

@ -1,15 +1,25 @@
use api::machine_capnp::machine::{ use crate::resources::modules::fabaccess::MachineState;
admin, admin::Server as AdminServer, use crate::resources::Resource;
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::session::SessionHandle; 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 { pub struct Machine {
session: SessionHandle, 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 { impl InfoServer for Machine {
@ -17,8 +27,8 @@ impl InfoServer for Machine {
&mut self, &mut self,
_: info::GetPropertyListParams, _: info::GetPropertyListParams,
_: info::GetPropertyListResults, _: info::GetPropertyListResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -26,38 +36,46 @@ impl InfoServer for Machine {
&mut self, &mut self,
_: info::GetReservationListParams, _: info::GetReservationListParams,
_: info::GetReservationListResults, _: info::GetReservationListResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
} }
impl UseServer for Machine { impl UseServer for Machine {
fn use_( fn use_(&mut self, _: use_::UseParams, _: use_::UseResults) -> Promise<(), ::capnp::Error> {
&mut self, let resource = self.resource.clone();
_: use_::UseParams, let session = self.session.clone();
_: use_::UseResults, Promise::from_future(async move {
) -> ::capnp::capability::Promise<(), ::capnp::Error> { let user = session.get_user();
::capnp::capability::Promise::err(::capnp::Error::unimplemented( resource.try_update(session, MachineState::used(user)).await;
"method not implemented".to_string(), Ok(())
)) })
} }
fn reserve( fn reserve(
&mut self, &mut self,
_: use_::ReserveParams, _: use_::ReserveParams,
_: use_::ReserveResults, _: use_::ReserveResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let resource = self.resource.clone();
"method not implemented".to_string(), 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( fn reserveto(
&mut self, &mut self,
_: use_::ReservetoParams, _: use_::ReservetoParams,
_: use_::ReservetoResults, _: use_::ReservetoResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -68,17 +86,21 @@ impl InUseServer for Machine {
&mut self, &mut self,
_: inuse::GiveBackParams, _: inuse::GiveBackParams,
_: inuse::GiveBackResults, _: inuse::GiveBackResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let resource = self.resource.clone();
"method not implemented".to_string(), let session = self.session.clone();
)) Promise::from_future(async move {
resource.give_back(session.clone()).await;
Ok(())
})
} }
fn send_raw_data( fn send_raw_data(
&mut self, &mut self,
_: inuse::SendRawDataParams, _: inuse::SendRawDataParams,
_: inuse::SendRawDataResults, _: inuse::SendRawDataResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -89,17 +111,18 @@ impl CheckServer for Machine {
&mut self, &mut self,
_: check::CheckParams, _: check::CheckParams,
_: check::CheckResults, _: check::CheckResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
fn reject( fn reject(
&mut self, &mut self,
_: check::RejectParams, _: check::RejectParams,
_: check::RejectResults, _: check::RejectResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -110,8 +133,8 @@ impl ManageServer for Machine {
&mut self, &mut self,
_: manage::GetMachineInfoExtendedParams, _: manage::GetMachineInfoExtendedParams,
_: manage::GetMachineInfoExtendedResults, _: manage::GetMachineInfoExtendedResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -119,8 +142,8 @@ impl ManageServer for Machine {
&mut self, &mut self,
_: manage::SetPropertyParams, _: manage::SetPropertyParams,
_: manage::SetPropertyResults, _: manage::SetPropertyResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -128,74 +151,104 @@ impl ManageServer for Machine {
&mut self, &mut self,
_: manage::RemovePropertyParams, _: manage::RemovePropertyParams,
_: manage::RemovePropertyResults, _: manage::RemovePropertyResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
fn force_use( fn force_use(
&mut self, &mut self,
_: manage::ForceUseParams, _: manage::ForceUseParams,
_: manage::ForceUseResults, _: manage::ForceUseResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let resource = self.resource.clone();
"method not implemented".to_string(), let session = self.session.clone();
)) Promise::from_future(async move {
resource
.force_set(MachineState::used(session.get_user()))
.await;
Ok(())
})
} }
fn force_free( fn force_free(
&mut self, &mut self,
_: manage::ForceFreeParams, _: manage::ForceFreeParams,
_: manage::ForceFreeResults, _: manage::ForceFreeResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let resource = self.resource.clone();
"method not implemented".to_string(), let session = self.session.clone();
)) Promise::from_future(async move {
resource.force_set(MachineState::free()).await;
Ok(())
})
} }
fn force_transfer( fn force_transfer(
&mut self, &mut self,
_: manage::ForceTransferParams, _: manage::ForceTransferParams,
_: manage::ForceTransferResults, _: manage::ForceTransferResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
fn block( fn block(
&mut self, &mut self,
_: manage::BlockParams, _: manage::BlockParams,
_: manage::BlockResults, _: manage::BlockResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let resource = self.resource.clone();
"method not implemented".to_string(), let session = self.session.clone();
)) Promise::from_future(async move {
resource
.force_set(MachineState::blocked(session.get_user()))
.await;
Ok(())
})
} }
fn disabled( fn disabled(
&mut self, &mut self,
_: manage::DisabledParams, _: manage::DisabledParams,
_: manage::DisabledResults, _: manage::DisabledResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( let mut resource = self.resource.clone();
"method not implemented".to_string(), Promise::from_future(async move {
)) resource.force_set(MachineState::disabled()).await;
Ok(())
})
} }
} }
impl AdminServer for Machine { impl AdminServer for Machine {
fn force_set_state( fn force_set_state(
&mut self, &mut self,
_: admin::ForceSetStateParams, params: admin::ForceSetStateParams,
_: admin::ForceSetStateResults, _: admin::ForceSetStateResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( use api::schema::machine_capnp::machine::MachineState as APIMState;
"method not implemented".to_string(), 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( fn force_set_user(
&mut self, &mut self,
_: admin::ForceSetUserParams, _: admin::ForceSetUserParams,
_: admin::ForceSetUserResults, _: admin::ForceSetUserResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -203,8 +256,8 @@ impl AdminServer for Machine {
&mut self, &mut self,
_: admin::GetAdminPropertyListParams, _: admin::GetAdminPropertyListParams,
_: admin::GetAdminPropertyListResults, _: admin::GetAdminPropertyListResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -212,8 +265,8 @@ impl AdminServer for Machine {
&mut self, &mut self,
_: admin::SetAdminPropertyParams, _: admin::SetAdminPropertyParams,
_: admin::SetAdminPropertyResults, _: admin::SetAdminPropertyResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }
@ -221,8 +274,8 @@ impl AdminServer for Machine {
&mut self, &mut self,
_: admin::RemoveAdminPropertyParams, _: admin::RemoveAdminPropertyParams,
_: admin::RemoveAdminPropertyResults, _: admin::RemoveAdminPropertyResults,
) -> ::capnp::capability::Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
::capnp::capability::Promise::err(::capnp::Error::unimplemented( Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(), "method not implemented".to_string(),
)) ))
} }

View File

@ -4,23 +4,29 @@ use api::machinesystem_capnp::machine_system::{
info, InfoParams, InfoResults, Server as MachineSystem, info, InfoParams, InfoResults, Server as MachineSystem,
}; };
use capnp::capability::Promise; 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 { pub struct Machines {
session: SessionHandle, session: SessionHandle,
resources: ResourcesHandle,
} }
impl Machines { impl Machines {
pub fn new(session: SessionHandle) -> Self { pub fn new(session: SessionHandle) -> Self {
Self { session } let resources = ResourcesHandle::new();
Self { session, resources }
} }
} }
impl MachineSystem for Machines { impl MachineSystem for Machines {
fn info(&mut self, _: InfoParams, _: InfoResults) -> Promise<(), ::capnp::Error> { fn info(&mut self, _: InfoParams, mut result: InfoResults) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented( // TODO permission checking
"method not implemented".to_string(), 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( fn get_machine_list(
&mut self, &mut self,
_: info::GetMachineListParams, _: info::GetMachineListParams,
_: info::GetMachineListResults, mut result: info::GetMachineListResults,
) -> Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented( let machine_list: Vec<(usize, &Resource)> = self.resources.list_all()
"method not implemented".to_string(), .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( fn get_machine(
&mut self, &mut self,
_: info::GetMachineParams, params: info::GetMachineParams,
_: info::GetMachineResults, mut result: info::GetMachineResults,
) -> Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented( let params = pry!(params.get());
"method not implemented".to_string(), 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( fn get_machine_u_r_n(
&mut self, &mut self,
_: info::GetMachineURNParams, params: info::GetMachineURNParams,
_: info::GetMachineURNResults, mut result: info::GetMachineURNResults,
) -> Promise<(), ::capnp::Error> { ) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented( let params = pry!(params.get());
"method not implemented".to_string(), 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(())
} }
} }

View File

@ -43,7 +43,7 @@ use rkyv::Deserialize;
use rkyv::ser::serializers::AlignedSerializer; use rkyv::ser::serializers::AlignedSerializer;
use std::sync::Arc; use std::sync::Arc;
use std::path::Path; use std::path::Path;
use crate::users::{User, UserDB}; use crate::users::db::{User, UserDB};
use std::collections::HashMap; use std::collections::HashMap;
use crate::resources::state::{OwnedEntry, State, db::StateDB}; use crate::resources::state::{OwnedEntry, State, db::StateDB};
use std::iter::FromIterator; use std::iter::FromIterator;

View File

@ -5,7 +5,7 @@ use async_channel as channel;
use async_oneshot as oneshot; use async_oneshot as oneshot;
use futures_signals::signal::Signal; use futures_signals::signal::Signal;
use futures_util::future::BoxFuture; 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::claim::{ResourceID, UserID};
use crate::resources::state::State; use crate::resources::state::State;

View File

@ -87,6 +87,8 @@ impl Diflouroborane {
SIGTERM, SIGTERM,
]).context("Failed to construct signal handler")?; ]).context("Failed to construct signal handler")?;
let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?;
let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?;

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use async_channel::Sender; use async_channel::Sender;
use lmdb::Environment; use lmdb::Environment;
use crate::resources::Update; use crate::resources::driver::Update;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Database of currently valid claims, interests and notify, as far as applicable /// Database of currently valid claims, interests and notify, as far as applicable

140
bffhd/resources/driver.rs Normal file
View File

@ -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<State, Error>;
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<State, Error> {
Ok(input.clone())
}
async fn shutdown(&mut self) {}
}
/// Error type a resources implementation can produce
#[derive(Debug)]
pub enum Error {
Internal(Box<dyn std::error::Error + Send>),
Denied,
}
// TODO: more message context
#[derive(Debug)]
pub struct Update {
pub state: State,
pub errchan: Sender<Error>,
}
#[derive(Debug)]
pub struct ResourceDriver {
// putput
res: Box<dyn ResourceModel>,
// input
rx: Receiver<Update>,
// output
db: StateAccessor,
signal: Mutable<State>,
}
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")
}
}

View File

@ -1,145 +1,113 @@
use std::fmt::Debug; use crate::resources::modules::fabaccess::{MachineState, Status};
use async_trait::async_trait; 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 claim;
pub mod db; 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; pub struct PermissionDenied;
/// - 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<State, Error>;
async fn shutdown(&mut self);
}
#[derive(Debug)] #[derive(Clone)]
pub struct Passthrough; pub struct Resource {}
#[async_trait]
impl ResourceModel for Passthrough { impl Resource {
async fn on_update(&mut self, input: &State) -> Result<State, Error> { pub fn get_state(&self) -> MachineState {
Ok(input.clone()) unimplemented!()
} }
async fn shutdown(&mut self) {} fn set_state(&self, state: MachineState) {
} unimplemented!()
}
/// Error type a resources implementation can produce fn set_previous_user(&self, user: User) {
#[derive(Debug)] unimplemented!()
pub enum Error { }
Internal(Box<dyn std::error::Error + Send>),
Denied,
}
// TODO: more message context pub async fn try_update(&self, session: SessionHandle, new: MachineState) {
#[derive(Debug)] let old = self.get_state();
pub struct Update { let user = session.get_user();
pub state: State,
pub errchan: Sender<Error>,
}
#[derive(Debug)] if session.has_manage(self) // Default allow for managers
pub struct ResourceDriver {
// putput
res: Box<dyn ResourceModel>,
// input || (session.has_write(self) // Decision tree for writers
rx: Receiver<Update>, && 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 // Reserving things for ourself is okay.
db: StateAccessor, (Status::Free, Status::Reserved(whom))
if &user == whom => true,
signal: Mutable<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,
impl ResourceDriver { // Un-reserving things we reserved is okay
pub async fn drive_to_end(&mut self) { (Status::Reserved(whom), Status::Free)
while let Ok(update) = self.rx.recv().await { if user == whom => true,
let state = update.state; // Using things that we've reserved is okay. But the person requesting
let mut errchan = update.errchan; // 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 { // Default is deny.
Ok(outstate) => { _ => false
// 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. // Default permissions everybody has
// Not applying the new state isn't correct either since we don't know what the || match (old.state, &new.state) {
// internal logic of the resources has done to make this happen. // Returning things we've been using is okay. This includes both if
// Another half right solution is to unwrap and recreate everything. // they're being freed or marked as to be checked.
// "Best" solution would be to tell the resources to rollback their interal (Status::InUse(who), Status::Free | Status::ToCheck(_)) if who == user => true,
// changes on a fatal failure and then notify the Claimant, while simply trying
// again for temporary failures. // Un-reserving things we reserved is okay
let _ = self.db.set(&state, &outstate); (Status::Reserved(whom), Status::Free) if user == whom => true,
self.signal.set(outstate);
}, // Default is deny.
Err(e) => { _ => false,
let _ = errchan.send(e); }
{
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);
}
}
}
pub async fn force_set(&self, new: MachineState) {
unimplemented!()
}
pub fn visible(&self, session: &SessionHandle) -> bool {
session.has_disclose(self) || self.is_owned_by(session.get_user())
}
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,
} }
} }
} }
#[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")
}
}

View File

@ -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<ObjectIdentifier> =
Lazy::new(|| ObjectIdentifier::from_str("1.3.6.1.4.1.48398.612.1.14").unwrap());
static OID_VALUE: Lazy<ObjectIdentifier> =
Lazy::new(|| ObjectIdentifier::from_str("1.3.6.1.4.1.48398.612.2.4").unwrap());
oidvalue!(OID_TYPE, MachineState, ArchivedMachineState);

View File

@ -0,0 +1,2 @@
pub mod fabaccess;

38
bffhd/resources/search.rs Normal file
View File

@ -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<Inner>,
}
impl ResourcesHandle {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner::new()),
}
}
pub fn list_all(&self) -> impl IntoIterator<Item=&Resource> {
unimplemented!();
&[]
}
pub fn get_by_id(&self, id: &str) -> Option<&Resource> {
unimplemented!()
}
pub fn get_by_urn(&self, urn: &str) -> Option<&Resource> {
unimplemented!()
}
}

View File

@ -1,24 +1,21 @@
use std::{ use std::{any::Any, fmt, hash::Hash, ptr, str::FromStr};
fmt,
ptr,
any::Any,
hash::Hash,
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 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 crate::utils::oid::ObjectIdentifier;
use rkyv::ser::{Serializer, ScratchSpace}; use inventory;
use std::collections::HashMap; use rkyv::ser::{ScratchSpace, Serializer};
use std::alloc::Layout;
use serde::ser::SerializeMap;
use std::fmt::Formatter;
use serde::de::Error as SerdeError; 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; use std::mem::MaybeUninit;
/// Adding a custom type to BFFH state management: /// Adding a custom type to BFFH state management:
@ -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 /// 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 /// you directly deserialize the type you're implementing `Value` for, but for manual
/// implementations this is important to keep in mind. /// implementations this is important to keep in mind.
fn deserialize_init<'de>(&mut self, deserializer: &mut dyn erased_serde::Deserializer<'de>) fn deserialize_init<'de>(
-> Result<(), erased_serde::Error>; &mut self,
deserializer: &mut dyn erased_serde::Deserializer<'de>,
) -> Result<(), erased_serde::Error>;
/// Implement `PartialEq` dynamically. /// Implement `PartialEq` dynamically.
/// ///
@ -55,19 +54,27 @@ erased_serde::serialize_trait_object!(SerializeValue);
erased_serde::serialize_trait_object!(DeserializeValue); erased_serde::serialize_trait_object!(DeserializeValue);
impl<T> Value for T impl<T> Value for T
where T: Any + fmt::Debug + PartialEq + Sync where
T: Any
+ fmt::Debug
+ PartialEq
+ Sync
+ erased_serde::Serialize + erased_serde::Serialize
+ for<'de> serde::Deserialize<'de> + for<'de> serde::Deserialize<'de>,
{ {
fn deserialize_init<'de>(&mut self, deserializer: &mut dyn erased_serde::Deserializer<'de>) fn deserialize_init<'de>(
-> Result<(), erased_serde::Error> &mut self,
{ deserializer: &mut dyn erased_serde::Deserializer<'de>,
) -> Result<(), erased_serde::Error> {
*self = erased_serde::deserialize(deserializer)?; *self = erased_serde::deserialize(deserializer)?;
Ok(()) Ok(())
} }
fn dyn_eq(&self, other: &dyn Value) -> bool { 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 { fn as_value(&self) -> &dyn Value {
@ -89,7 +96,8 @@ impl PartialEq for dyn Value {
pub(super) struct DynVal<'a>(pub &'a dyn SerializeValue); pub(super) struct DynVal<'a>(pub &'a dyn SerializeValue);
impl<'a> serde::Serialize for DynVal<'a> { impl<'a> serde::Serialize for DynVal<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer where
S: serde::Serializer,
{ {
let mut ser = serializer.serialize_map(Some(1))?; let mut ser = serializer.serialize_map(Some(1))?;
let oid = self.0.archived_type_oid(); let oid = self.0.archived_type_oid();
@ -101,7 +109,8 @@ impl<'a> serde::Serialize for DynVal<'a> {
pub(super) struct DynOwnedVal(pub Box<dyn SerializeValue>); pub(super) struct DynOwnedVal(pub Box<dyn SerializeValue>);
impl<'de> serde::Deserialize<'de> for DynOwnedVal { impl<'de> serde::Deserialize<'de> for DynOwnedVal {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> where
D: serde::Deserializer<'de>,
{ {
deserializer.deserialize_map(DynValVisitor) 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") write!(formatter, "an one entry map from OID to some value object")
} }
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
{
// Bad magic code. Problem we have to solve: We only know how to parse whatever comes // 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 // after the OID after having looked at the OID. We have zero static type info available
// during deserialization. So: // during deserialization. So:
// Get OID first. That's easy, we know it's the key, we know how to read it. // Get OID first. That's easy, we know it's the key, we know how to read it.
let oid: ObjectIdentifier = map.next_key()? let oid: ObjectIdentifier = map.next_key()?.ok_or(A::Error::missing_field("oid"))?;
.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. // 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)) let valimpl = IMPL_REGISTRY.get(ImplId::from_type_oid(&oid)).ok_or(
.ok_or(serde::de::Error::invalid_value( serde::de::Error::invalid_value(
serde::de::Unexpected::Other("unknown oid"), serde::de::Unexpected::Other("unknown oid"),
&"oid an implementation was registered for", &"oid an implementation was registered for",
))?; ),
)?;
// Casting random usize you find on the side of the road as vtable on unchecked pointers. // Casting random usize you find on the side of the road as vtable on unchecked pointers.
// What could possibly go wrong? >:D // 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? // validate in any other way if this is sane?
// Well... // Well...
let ptr: *mut () = std::alloc::alloc(layout).cast::<()>(); let ptr: *mut () = std::alloc::alloc(layout).cast::<()>();
let b = Box::from_raw(ptr_meta::from_raw_parts_mut( let b = Box::from_raw(ptr_meta::from_raw_parts_mut(ptr, meta));
ptr,
meta));
// We make this a MaybeUninit so `Drop` is never called on the uninitialized value // We make this a MaybeUninit so `Drop` is never called on the uninitialized value
MaybeUninit::new(b) MaybeUninit::new(b)
@ -173,7 +179,8 @@ impl<'de> serde::de::DeserializeSeed<'de> for InitIntoSelf {
type Value = Box<dyn SerializeValue>; type Value = Box<dyn SerializeValue>;
fn deserialize<D>(mut self, deserializer: D) -> Result<Self::Value, D::Error> fn deserialize<D>(mut self, deserializer: D) -> Result<Self::Value, D::Error>
where D: serde::Deserializer<'de> where
D: serde::Deserializer<'de>,
{ {
let mut deser = <dyn erased_serde::Deserializer>::erase(deserializer); let mut deser = <dyn erased_serde::Deserializer>::erase(deserializer);
@ -182,8 +189,9 @@ impl<'de> serde::de::DeserializeSeed<'de> for InitIntoSelf {
let selfptr = unsafe { &mut *self.0.as_mut_ptr() }; let selfptr = unsafe { &mut *self.0.as_mut_ptr() };
// Hey, better initialize late than never. // Hey, better initialize late than never.
selfptr.deserialize_init(&mut deser).map_err(|e| selfptr
D::Error::custom(e))?; .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. // Assuming `deserialize_init` didn't error and did its job this is now safe.
unsafe { Ok(self.0.assume_init()) } unsafe { Ok(self.0.assume_init()) }
@ -208,7 +216,6 @@ impl<S: ScratchSpace + Serializer + ?Sized> SerializeUnsized<S> for dyn Serializ
} }
} }
/// Serialize dynamic types by storing an OID alongside /// Serialize dynamic types by storing an OID alongside
pub trait SerializeDynOid { pub trait SerializeDynOid {
fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result<usize, DynError>; fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result<usize, DynError>;
@ -216,7 +223,8 @@ pub trait SerializeDynOid {
} }
impl<T> SerializeDynOid for T impl<T> SerializeDynOid for T
where T: for<'a> Serialize<dyn DynSerializer + 'a>, where
T: for<'a> Serialize<dyn DynSerializer + 'a>,
T::Archived: TypeOid, T::Archived: TypeOid,
{ {
fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result<usize, DynError> { fn serialize_dynoid(&self, serializer: &mut dyn DynSerializer) -> Result<usize, DynError> {
@ -247,8 +255,8 @@ pub trait SerializeValue: Value + SerializeDynOid {
} }
impl<T: Archive + Value + SerializeDynOid + Clone> SerializeValue for T impl<T: Archive + Value + SerializeDynOid + Clone> SerializeValue for T
where where
T::Archived: RegisteredImpl T::Archived: RegisteredImpl,
{ {
fn dyn_clone(&self) -> Box<dyn SerializeValue> { fn dyn_clone(&self) -> Box<dyn SerializeValue> {
Box::new(self.clone()) Box::new(self.clone())
@ -278,17 +286,21 @@ impl ArchivePointee for dyn DeserializeValue {
} }
} }
impl<D: Fallible + ?Sized> DeserializeUnsized<dyn SerializeValue, D> for dyn DeserializeValue { impl<D: Fallible + ?Sized> DeserializeUnsized<dyn SerializeValue, D> for dyn DeserializeValue {
unsafe fn deserialize_unsized(&self, unsafe fn deserialize_unsized(
&self,
mut deserializer: &mut D, mut deserializer: &mut D,
mut alloc: impl FnMut(Layout) -> *mut u8 mut alloc: impl FnMut(Layout) -> *mut u8,
) -> Result<*mut (), D::Error> { ) -> 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) fn deserialize_metadata(
-> Result<<dyn SerializeValue as Pointee>::Metadata, D::Error> &self,
{ mut deserializer: &mut D,
self.deserialize_dynoid_metadata(&mut deserializer).map_err(|e| *e.downcast().unwrap()) ) -> Result<<dyn SerializeValue as Pointee>::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 Archived = dyn DeserializeValue;
type MetadataResolver = <ObjectIdentifier as Archive>::Resolver; type MetadataResolver = <ObjectIdentifier as Archive>::Resolver;
unsafe fn resolve_metadata(&self, pos: usize, resolver: Self::MetadataResolver, out: *mut ArchivedMetadata<Self>) { unsafe fn resolve_metadata(
&self,
pos: usize,
resolver: Self::MetadataResolver,
out: *mut ArchivedMetadata<Self>,
) {
let (oid_pos, oid) = out_field!(out.type_oid); let (oid_pos, oid) = out_field!(out.type_oid);
let type_oid = self.archived_type_oid(); let type_oid = self.archived_type_oid();
type_oid.resolve(pos + oid_pos, resolver, oid); type_oid.resolve(pos + oid_pos, resolver, oid);
@ -316,10 +333,14 @@ impl ArchivedValueMetadata {
pub fn vtable(&self) -> usize { pub fn vtable(&self) -> usize {
IMPL_REGISTRY 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 \ type \
oid \ oid \
{:?}", self.type_oid)) {:?}",
self.type_oid
))
.vtable .vtable
} }
@ -342,7 +363,7 @@ impl<'a> ImplId<'a> {
impl ImplId<'static> { impl ImplId<'static> {
fn new<T: TypeOid>() -> Self { fn new<T: TypeOid>() -> Self {
Self { Self {
type_oid: &T::type_oid() type_oid: &T::type_oid(),
} }
} }
} }
@ -362,15 +383,6 @@ pub struct ImplDebugInfo {
pub line: u32, pub line: u32,
pub column: u32, pub column: u32,
} }
macro_rules! debug_info {
() => {
ImplDebugInfo {
file: core::file!(),
line: core::line!(),
column: core::column!(),
}
}
}
impl ImplData<'_> { impl ImplData<'_> {
pub unsafe fn pointer_metadata<T: ?Sized>(&self) -> DynMetadata<T> { pub unsafe fn pointer_metadata<T: ?Sized>(&self) -> DynMetadata<T> {
@ -407,7 +419,9 @@ struct ImplRegistry {
impl ImplRegistry { impl ImplRegistry {
fn new() -> Self { fn new() -> Self {
Self { oid_to_data: HashMap::new() } Self {
oid_to_data: HashMap::new(),
}
} }
fn add_entry(&mut self, entry: &'static ImplEntry) { fn add_entry(&mut self, entry: &'static ImplEntry) {
@ -415,10 +429,14 @@ impl ImplRegistry {
if let Some(old) = old_val { if let Some(old) = old_val {
eprintln!("Value impl oid conflict for {:?}", entry.id.type_oid); eprintln!("Value impl oid conflict for {:?}", entry.id.type_oid);
eprintln!("Existing impl registered at {}:{}:{}", eprintln!(
old.info.file, old.info.line, old.info.column); "Existing impl registered at {}:{}:{}",
eprintln!("New impl registered at {}:{}:{}", old.info.file, old.info.line, old.info.column
entry.data.info.file, entry.data.info.line, entry.data.info.column); );
eprintln!(
"New impl registered at {}:{}:{}",
entry.data.info.file, entry.data.info.line, entry.data.info.column
);
} }
assert!(old_val.is_none()); assert!(old_val.is_none());
} }
@ -444,37 +462,60 @@ pub unsafe trait RegisteredImpl {
fn debug_info() -> ImplDebugInfo; fn debug_info() -> ImplDebugInfo;
} }
macro_rules! oiddeser { #[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 ) => { ( $y:ty, $z:ty ) => {
impl DeserializeDynOid for $y impl $crate::resources::state::value::DeserializeDynOid for $y
where $y: for<'a> Deserialize<$z, (dyn DynDeserializer + 'a)> where
$y: for<'a> ::rkyv::Deserialize<$z, (dyn ::rkyv_dyn::DynDeserializer + 'a)>,
{ {
unsafe fn deserialize_dynoid(&self, deserializer: &mut dyn DynDeserializer, alloc: &mut dyn FnMut(Layout) -> *mut u8) -> Result<*mut (), DynError> { unsafe fn deserialize_dynoid(
let ptr = alloc(Layout::new::<$z>()).cast::<$z>(); &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)?); ptr.write(self.deserialize(deserializer)?);
Ok(ptr as *mut ()) Ok(ptr as *mut ())
} }
fn deserialize_dynoid_metadata(&self, _: &mut dyn DynDeserializer) -> Result<<dyn fn deserialize_dynoid_metadata(
SerializeValue as Pointee>::Metadata, DynError> { &self,
_: &mut dyn ::rkyv_dyn::DynDeserializer,
) -> ::std::result::Result<<dyn $crate::resources::state::value::SerializeValue
as
::ptr_meta::Pointee>::Metadata, ::rkyv_dyn::DynError> {
unsafe { unsafe {
Ok(core::mem::transmute(ptr_meta::metadata( Ok(core::mem::transmute(ptr_meta::metadata(
core::ptr::null::<$z>() as *const dyn SerializeValue ::core::ptr::null::<$z>() as *const dyn $crate::resources::state::value::SerializeValue,
))) )))
} }
} }
} }
};
} }
} #[macro_export]
macro_rules! oidvalue { macro_rules! oidvalue {
( $x:ident, $y:ty ) => { ( $x:ident, $y:ty ) => {
oidvalue! {$x, $y, $y} $crate::oidvalue! {$x, $y, $y}
}; };
( $x:ident, $y:ty, $z:ty ) => { ( $x:ident, $y:ty, $z:ty ) => {
oiddeser! {$z, $y} $crate::oiddeser! {$z, $y}
impl TypeOid for $z { impl $crate::resources::state::value::TypeOid for $z {
fn type_oid() -> &'static ObjectIdentifier { fn type_oid() -> &'static $crate::utils::oid::ObjectIdentifier {
&$x &$x
} }
@ -486,22 +527,24 @@ macro_rules! oidvalue {
"builtin" "builtin"
} }
} }
unsafe impl RegisteredImpl for $z { unsafe impl $crate::resources::state::value::RegisteredImpl for $z {
fn vtable() -> usize { fn vtable() -> usize {
unsafe { unsafe {
core::mem::transmute(ptr_meta::metadata( ::core::mem::transmute(ptr_meta::metadata(
core::ptr::null::<$z>() as *const dyn DeserializeValue ::core::ptr::null::<$z>() as *const dyn $crate::resources::state::value::DeserializeValue
)) ))
} }
} }
fn debug_info() -> ImplDebugInfo { fn debug_info() -> $crate::resources::state::value::ImplDebugInfo {
debug_info!() $crate::debug_info!()
} }
} }
inventory::submit! {ImplEntry::new::<$z>()} ::inventory::submit! {$crate::resources::state::value::ImplEntry::new::<$z>()}
};
} }
} }
use macros::*;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref OID_BOOL: ObjectIdentifier = { pub static ref OID_BOOL: ObjectIdentifier = {
@ -563,9 +606,20 @@ oidvalue!(OID_I32, i32);
oidvalue!(OID_I64, i64); oidvalue!(OID_I64, i64);
oidvalue!(OID_I128, i128); oidvalue!(OID_I128, i128);
#[derive(serde::Serialize, serde::Deserialize)] #[derive(
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] serde::Serialize,
#[archive_attr(derive(TypeName, Debug, PartialEq, serde::Serialize, serde::Deserialize))] 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 struct Vec3u8 {
pub a: u8, pub a: u8,
pub b: u8, pub b: u8,
@ -576,9 +630,9 @@ oidvalue!(OID_VEC3U8, Vec3u8, ArchivedVec3u8);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use rand::Rng;
use rand::distributions::Standard; use rand::distributions::Standard;
use rand::prelude::Distribution; use rand::prelude::Distribution;
use rand::Rng;
impl Distribution<Vec3u8> for Standard { impl Distribution<Vec3u8> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3u8 { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3u8 {

View File

@ -1,4 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::authorization::roles::Role;
use crate::resources::Resource;
use crate::users::User;
struct Inner { struct Inner {
@ -29,4 +32,20 @@ pub struct SessionHandle {
} }
impl 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!()
}
} }

View File

@ -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::collections::HashSet;
use std::sync::Arc; 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<String>,
}
type Adapter = AllocAdapter<User>; type Adapter = AllocAdapter<User>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -64,59 +80,3 @@ impl UserDB {
Ok(out) Ok(out)
} }
} }
pub struct UserIndex {
env: Arc<Environment>,
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(())
}
}

View File

@ -14,24 +14,43 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use rkyv::{Archive, Deserialize, Infallible, Serialize};
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; 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; 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 { pub struct User {
id: u128, id: u64
username: String,
roles: Vec<String>,
} }
impl User { impl User {
pub fn new(id: u128, username: String, roles: Vec<String>) -> Self { pub fn new(id: u64) -> Self {
User { id, username, roles } User { id }
}
pub fn get_username(&self) -> &str {
unimplemented!()
}
pub fn get_roles(&self) -> impl IntoIterator<Item=Role> {
unimplemented!();
[]
} }
} }