mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-22 14:57:56 +01:00
Merge branch 'feature/spanned-api-handling' into development
* feature/spanned-api-handling: Output plentiful trace info for API calls log all api calls with `TRACE` level Add a connection-specific span to each API handler
This commit is contained in:
commit
e1377d0f79
@ -1,9 +1,13 @@
|
|||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp::Error;
|
use capnp::Error;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::property::AuthId;
|
use rsasl::property::AuthId;
|
||||||
use rsasl::session::{Session, Step};
|
use rsasl::session::{Session, Step};
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Formatter, Write};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::capnp::session::APISession;
|
use crate::capnp::session::APISession;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
@ -12,19 +16,46 @@ use api::authenticationsystem_capnp::authentication::{
|
|||||||
};
|
};
|
||||||
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::authenticationsystem";
|
||||||
|
|
||||||
pub struct Authentication {
|
pub struct Authentication {
|
||||||
|
span: Span,
|
||||||
state: State,
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Authentication {
|
impl Authentication {
|
||||||
pub fn new(session: Session, sessionmanager: SessionManager) -> Self {
|
pub fn new(
|
||||||
|
parent: &Span,
|
||||||
|
mechanism: &Mechname, /* TODO: this is stored in session as well, get it out of there. */
|
||||||
|
session: Session,
|
||||||
|
sessionmanager: SessionManager,
|
||||||
|
) -> Self {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: parent,
|
||||||
|
"Authentication",
|
||||||
|
mechanism = mechanism.as_str()
|
||||||
|
);
|
||||||
|
tracing::trace!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &span,
|
||||||
|
"constructing valid authentication system"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
state: State::Running(session, sessionmanager),
|
state: State::Running(session, sessionmanager),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_mechanism() -> Self {
|
pub fn invalid_mechanism() -> Self {
|
||||||
|
let span = tracing::info_span!(target: TARGET, "Authentication",);
|
||||||
|
tracing::trace!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &span,
|
||||||
|
"constructing invalid mechanism authentication system"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
state: State::InvalidMechanism,
|
state: State::InvalidMechanism,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +75,19 @@ impl Authentication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Authentication {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("Authentication(")?;
|
||||||
|
match &self.state {
|
||||||
|
State::InvalidMechanism => f.write_str("invalid mechanism")?,
|
||||||
|
State::Finished => f.write_str("finished")?,
|
||||||
|
State::Aborted => f.write_str("aborted")?,
|
||||||
|
State::Running(_, _) => f.write_str("running")?,
|
||||||
|
}
|
||||||
|
f.write_char(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
InvalidMechanism,
|
InvalidMechanism,
|
||||||
Finished,
|
Finished,
|
||||||
@ -53,13 +97,30 @@ enum State {
|
|||||||
|
|
||||||
impl AuthenticationSystem for Authentication {
|
impl AuthenticationSystem for Authentication {
|
||||||
fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> {
|
fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> {
|
||||||
let span = tracing::trace_span!("step");
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(target: TARGET, "step",).entered();
|
||||||
|
|
||||||
|
tracing::trace!(params.data = "<authentication data>", "method call");
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Response {
|
||||||
|
union_field: &'static str,
|
||||||
|
}
|
||||||
|
impl fmt::Display for Response {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("Response(")?;
|
||||||
|
f.write_str(self.union_field)?;
|
||||||
|
f.write_char(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut response;
|
||||||
|
|
||||||
let mut builder = results.get();
|
let mut builder = results.get();
|
||||||
if let State::Running(mut session, manager) =
|
if let State::Running(mut session, manager) =
|
||||||
std::mem::replace(&mut self.state, State::Aborted)
|
std::mem::replace(&mut self.state, State::Aborted)
|
||||||
{
|
{
|
||||||
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
||||||
|
|
||||||
let mut out = Cursor::new(Vec::new());
|
let mut out = Cursor::new(Vec::new());
|
||||||
match session.step(Some(data), &mut out) {
|
match session.step(Some(data), &mut out) {
|
||||||
Ok(Step::Done(data)) => {
|
Ok(Step::Done(data)) => {
|
||||||
@ -71,11 +132,15 @@ impl AuthenticationSystem for Authentication {
|
|||||||
"Authentication didn't provide an authid as required".to_string(),
|
"Authentication didn't provide an authid as required".to_string(),
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
let session = pry!(manager.open(uid.as_ref()).ok_or_else(|| {
|
let session = pry!(manager.open(&self.span, uid.as_ref()).ok_or_else(|| {
|
||||||
tracing::warn!(uid = uid.as_str(), "Failed to lookup the given user");
|
tracing::warn!(uid = uid.as_str(), "Failed to lookup the given user");
|
||||||
capnp::Error::failed("Failed to lookup the given user".to_string())
|
capnp::Error::failed("Failed to lookup the given user".to_string())
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "successful",
|
||||||
|
};
|
||||||
|
|
||||||
let mut builder = builder.init_successful();
|
let mut builder = builder.init_successful();
|
||||||
if data.is_some() {
|
if data.is_some() {
|
||||||
builder.set_additional_data(out.into_inner().as_slice());
|
builder.set_additional_data(out.into_inner().as_slice());
|
||||||
@ -86,21 +151,49 @@ impl AuthenticationSystem for Authentication {
|
|||||||
Ok(Step::NeedsMore(_)) => {
|
Ok(Step::NeedsMore(_)) => {
|
||||||
self.state = State::Running(session, manager);
|
self.state = State::Running(session, manager);
|
||||||
builder.set_challenge(out.into_inner().as_slice());
|
builder.set_challenge(out.into_inner().as_slice());
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "challenge",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.state = State::Aborted;
|
self.state = State::Aborted;
|
||||||
self.build_error(builder);
|
self.build_error(builder);
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.build_error(builder);
|
self.build_error(builder);
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results = %response,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> {
|
fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"abort",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
self.state = State::Aborted;
|
self.state = State::Aborted;
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use api::connection_capnp::bootstrap;
|
use api::connection_capnp::bootstrap;
|
||||||
pub use api::connection_capnp::bootstrap::Client;
|
pub use api::connection_capnp::bootstrap::Client;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Formatter, Write};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use crate::authentication::AuthenticationHandle;
|
use crate::authentication::AuthenticationHandle;
|
||||||
@ -8,12 +10,14 @@ use crate::session::SessionManager;
|
|||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
/// Cap'n Proto API Handler
|
/// Cap'n Proto API Handler
|
||||||
pub struct BootCap {
|
pub struct BootCap {
|
||||||
peer_addr: SocketAddr,
|
peer_addr: SocketAddr,
|
||||||
authentication: AuthenticationHandle,
|
authentication: AuthenticationHandle,
|
||||||
sessionmanager: SessionManager,
|
sessionmanager: SessionManager,
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootCap {
|
impl BootCap {
|
||||||
@ -21,12 +25,13 @@ impl BootCap {
|
|||||||
peer_addr: SocketAddr,
|
peer_addr: SocketAddr,
|
||||||
authentication: AuthenticationHandle,
|
authentication: AuthenticationHandle,
|
||||||
sessionmanager: SessionManager,
|
sessionmanager: SessionManager,
|
||||||
|
span: Span,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
tracing::trace!(%peer_addr, "bootstrapping RPC");
|
|
||||||
Self {
|
Self {
|
||||||
peer_addr,
|
peer_addr,
|
||||||
authentication,
|
authentication,
|
||||||
sessionmanager,
|
sessionmanager,
|
||||||
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,8 +42,14 @@ impl bootstrap::Server for BootCap {
|
|||||||
_: bootstrap::GetAPIVersionParams,
|
_: bootstrap::GetAPIVersionParams,
|
||||||
_: bootstrap::GetAPIVersionResults,
|
_: bootstrap::GetAPIVersionResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("get_api_version", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"Bootstrap",
|
||||||
|
method = "getAPIVersion",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +58,24 @@ impl bootstrap::Server for BootCap {
|
|||||||
_: bootstrap::GetServerReleaseParams,
|
_: bootstrap::GetServerReleaseParams,
|
||||||
mut result: bootstrap::GetServerReleaseResults,
|
mut result: bootstrap::GetServerReleaseResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("get_server_release", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"Bootstrap",
|
||||||
|
method = "getServerRelease",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
builder.set_name("bffhd");
|
builder.set_name("bffhd");
|
||||||
builder.set_release(crate::env::VERSION);
|
builder.set_release(crate::env::VERSION);
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results.name = "bffhd",
|
||||||
|
results.release = crate::env::VERSION,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,10 +84,13 @@ impl bootstrap::Server for BootCap {
|
|||||||
_params: bootstrap::MechanismsParams,
|
_params: bootstrap::MechanismsParams,
|
||||||
mut result: bootstrap::MechanismsResults,
|
mut result: bootstrap::MechanismsResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("mechanisms", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
tracing::trace!("mechanisms");
|
"mechanisms",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!(target: "bffh::api", "method call");
|
||||||
|
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
let mechs: Vec<_> = self
|
let mechs: Vec<_> = self
|
||||||
@ -77,6 +104,28 @@ impl bootstrap::Server for BootCap {
|
|||||||
mechbuilder.set(i as u32, m);
|
mechbuilder.set(i as u32, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DisMechs<'a>(Vec<&'a str>);
|
||||||
|
impl fmt::Display for DisMechs<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_char('[')?;
|
||||||
|
let mut first = true;
|
||||||
|
for mechanism in self.0.iter() {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
f.write_str(mechanism)?;
|
||||||
|
} else {
|
||||||
|
f.write_str(" ,")?;
|
||||||
|
f.write_str(mechanism)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_char(']')?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracing::trace!(
|
||||||
|
results.mechs = %DisMechs(mechs),
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,18 +134,22 @@ impl bootstrap::Server for BootCap {
|
|||||||
params: bootstrap::CreateSessionParams,
|
params: bootstrap::CreateSessionParams,
|
||||||
mut result: bootstrap::CreateSessionResults,
|
mut result: bootstrap::CreateSessionResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("create_session", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"createSession",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let mechanism: &str = pry!(params.get_mechanism());
|
let mechanism: &str = pry!(params.get_mechanism());
|
||||||
|
|
||||||
tracing::trace!(mechanism);
|
tracing::trace!(params.mechanism = mechanism, "method call");
|
||||||
|
|
||||||
let mechname = Mechname::new(mechanism.as_bytes());
|
let mechname = Mechname::new(mechanism.as_bytes());
|
||||||
let auth = if let Ok(mechname) = mechname {
|
let auth = if let Ok(mechname) = mechname {
|
||||||
if let Ok(session) = self.authentication.start(mechname) {
|
if let Ok(session) = self.authentication.start(mechname) {
|
||||||
Authentication::new(session, self.sessionmanager.clone())
|
Authentication::new(&self.span, mechname, session, self.sessionmanager.clone())
|
||||||
} else {
|
} else {
|
||||||
Authentication::invalid_mechanism()
|
Authentication::invalid_mechanism()
|
||||||
}
|
}
|
||||||
@ -104,6 +157,11 @@ impl bootstrap::Server for BootCap {
|
|||||||
Authentication::invalid_mechanism()
|
Authentication::invalid_mechanism()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results.authentication = %auth,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
|
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
builder.set_authentication(capnp_rpc::new_client(auth));
|
builder.set_authentication(capnp_rpc::new_client(auth));
|
||||||
|
|
||||||
|
@ -6,17 +6,27 @@ use crate::RESOURCES;
|
|||||||
use api::machinesystem_capnp::machine_system::info;
|
use api::machinesystem_capnp::machine_system::info;
|
||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::machinesystem";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Machines {
|
pub struct Machines {
|
||||||
|
span: Span,
|
||||||
session: SessionHandle,
|
session: SessionHandle,
|
||||||
resources: ResourcesHandle,
|
resources: ResourcesHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machines {
|
impl Machines {
|
||||||
pub fn new(session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &session.span,
|
||||||
|
"MachineSystem",
|
||||||
|
);
|
||||||
// FIXME no unwrap bad
|
// FIXME no unwrap bad
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
session,
|
session,
|
||||||
resources: RESOURCES.get().unwrap().clone(),
|
resources: RESOURCES.get().unwrap().clone(),
|
||||||
}
|
}
|
||||||
@ -29,6 +39,16 @@ impl info::Server for Machines {
|
|||||||
_: info::GetMachineListParams,
|
_: info::GetMachineListParams,
|
||||||
mut result: info::GetMachineListResults,
|
mut result: info::GetMachineListResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachineList",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let machine_list: Vec<(usize, &Resource)> = self
|
let machine_list: Vec<(usize, &Resource)> = self
|
||||||
.resources
|
.resources
|
||||||
.list_all()
|
.list_all()
|
||||||
@ -43,6 +63,9 @@ impl info::Server for Machines {
|
|||||||
Machine::build(self.session.clone(), resource, mbuilder);
|
Machine::build(self.session.clone(), resource, mbuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: indicate result?
|
||||||
|
tracing::trace!("method return");
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +74,25 @@ impl info::Server for Machines {
|
|||||||
params: info::GetMachineParams,
|
params: info::GetMachineParams,
|
||||||
mut result: info::GetMachineResults,
|
mut result: info::GetMachineResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachine",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let id = pry!(params.get_id());
|
let id = pry!(params.get_id());
|
||||||
|
|
||||||
|
tracing::trace!(params.id = id, "method call");
|
||||||
|
|
||||||
if let Some(resource) = self.resources.get_by_id(id) {
|
if let Some(resource) = self.resources.get_by_id(id) {
|
||||||
|
tracing::trace!(results = "Just", results.inner = id, "method return");
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
||||||
|
} else {
|
||||||
|
tracing::trace!(results = "Nothing", "method return");
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
@ -67,12 +103,29 @@ impl info::Server for Machines {
|
|||||||
params: info::GetMachineURNParams,
|
params: info::GetMachineURNParams,
|
||||||
mut result: info::GetMachineURNResults,
|
mut result: info::GetMachineURNResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachineURN",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let urn = pry!(params.get_urn());
|
let urn = pry!(params.get_urn());
|
||||||
|
|
||||||
|
tracing::trace!(params.urn = urn, "method call");
|
||||||
|
|
||||||
if let Some(resource) = self.resources.get_by_urn(urn) {
|
if let Some(resource) = self.resources.get_by_urn(urn) {
|
||||||
|
tracing::trace!(
|
||||||
|
results = "Just",
|
||||||
|
results.inner = resource.get_id(),
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
||||||
|
} else {
|
||||||
|
tracing::trace!(results = "Nothing", "method return");
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
|
@ -12,7 +12,7 @@ use futures_util::{stream, AsyncRead, AsyncWrite, FutureExt, StreamExt};
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
use crate::authentication::AuthenticationHandle;
|
use crate::authentication::AuthenticationHandle;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
@ -145,12 +145,36 @@ impl APIServer {
|
|||||||
peer_addr: SocketAddr,
|
peer_addr: SocketAddr,
|
||||||
stream: impl Future<Output = io::Result<TlsStream<IO>>>,
|
stream: impl Future<Output = io::Result<TlsStream<IO>>>,
|
||||||
) {
|
) {
|
||||||
tracing::debug!("handling new API connection");
|
let span = tracing::trace_span!("api.handle");
|
||||||
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
struct Peer {
|
||||||
|
ip: IpAddr,
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer = Peer {
|
||||||
|
ip: peer_addr.ip(),
|
||||||
|
port: peer_addr.port(),
|
||||||
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
%peer.ip,
|
||||||
|
peer.port,
|
||||||
|
"spawning api handler"
|
||||||
|
);
|
||||||
|
|
||||||
|
let connection_span = tracing::info_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"connection",
|
||||||
|
%peer.ip,
|
||||||
|
peer.port,
|
||||||
|
);
|
||||||
let f = async move {
|
let f = async move {
|
||||||
|
tracing::trace!(parent: &connection_span, "starting tls exchange");
|
||||||
let stream = match stream.await {
|
let stream = match stream.await {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
Err(e) => {
|
Err(error) => {
|
||||||
tracing::error!("TLS handshake failed: {}", e);
|
tracing::error!(parent: &connection_span, %error, "TLS handshake failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -161,10 +185,15 @@ impl APIServer {
|
|||||||
peer_addr,
|
peer_addr,
|
||||||
self.authentication.clone(),
|
self.authentication.clone(),
|
||||||
self.sessionmanager.clone(),
|
self.sessionmanager.clone(),
|
||||||
|
connection_span.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
if let Err(e) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await {
|
if let Err(error) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await {
|
||||||
tracing::error!("Error during RPC handling: {}", e);
|
tracing::error!(
|
||||||
|
parent: &connection_span,
|
||||||
|
%error,
|
||||||
|
"error occured during rpc handling",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cgroup = SupervisionRegistry::with(SupervisionRegistry::new_group);
|
let cgroup = SupervisionRegistry::with(SupervisionRegistry::new_group);
|
||||||
|
@ -5,16 +5,22 @@ use api::permissionsystem_capnp::permission_system::info::{
|
|||||||
};
|
};
|
||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp::Error;
|
use capnp::Error;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::permissionsystem";
|
||||||
|
|
||||||
pub struct Permissions {
|
pub struct Permissions {
|
||||||
|
span: Span,
|
||||||
roles: Roles,
|
roles: Roles,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Permissions {
|
impl Permissions {
|
||||||
pub fn new(session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
|
let span = tracing::info_span!(target: TARGET, "PermissionSystem",);
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
roles: session.roles,
|
roles: session.roles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,6 +32,10 @@ impl PermissionSystem for Permissions {
|
|||||||
_: GetRoleListParams,
|
_: GetRoleListParams,
|
||||||
mut results: GetRoleListResults,
|
mut results: GetRoleListResults,
|
||||||
) -> Promise<(), Error> {
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getRoleList",).entered();
|
||||||
|
|
||||||
|
tracing::trace!("method call");
|
||||||
let roles = self.roles.list().collect::<Vec<&String>>();
|
let roles = self.roles.list().collect::<Vec<&String>>();
|
||||||
let mut builder = results.get();
|
let mut builder = results.get();
|
||||||
let mut b = builder.init_role_list(roles.len() as u32);
|
let mut b = builder.init_role_list(roles.len() as u32);
|
||||||
@ -33,6 +43,7 @@ impl PermissionSystem for Permissions {
|
|||||||
let mut role_builder = b.reborrow().get(i as u32);
|
let mut role_builder = b.reborrow().get(i as u32);
|
||||||
role_builder.set_name(role);
|
role_builder.set_name(role);
|
||||||
}
|
}
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
use api::usersystem_capnp::user_system::{info, manage, search};
|
use api::usersystem_capnp::user_system::{info, manage, search};
|
||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::capnp::user::User;
|
use crate::capnp::user::User;
|
||||||
|
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::usersystem";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Users {
|
pub struct Users {
|
||||||
|
span: Span,
|
||||||
session: SessionHandle,
|
session: SessionHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Users {
|
impl Users {
|
||||||
pub fn new(session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
Self { session }
|
let span = tracing::info_span!(target: TARGET, "UserSystem",);
|
||||||
|
Self { span, session }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,8 +29,14 @@ impl info::Server for Users {
|
|||||||
_: info::GetUserSelfParams,
|
_: info::GetUserSelfParams,
|
||||||
mut result: info::GetUserSelfResults,
|
mut result: info::GetUserSelfResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserSelf").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
User::build(self.session.clone(), builder);
|
User::build(self.session.clone(), builder);
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,6 +47,10 @@ impl manage::Server for Users {
|
|||||||
_: manage::GetUserListParams,
|
_: manage::GetUserListParams,
|
||||||
mut result: manage::GetUserListResults,
|
mut result: manage::GetUserListResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserList",).entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let userdb = self.session.users.into_inner();
|
let userdb = self.session.users.into_inner();
|
||||||
let users = pry!(userdb
|
let users = pry!(userdb
|
||||||
.get_all()
|
.get_all()
|
||||||
@ -44,18 +59,30 @@ impl manage::Server for Users {
|
|||||||
for (i, (_, user)) in users.into_iter().enumerate() {
|
for (i, (_, user)) in users.into_iter().enumerate() {
|
||||||
User::fill(&self.session, user, builder.reborrow().get(i as u32));
|
User::fill(&self.session, user, builder.reborrow().get(i as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_user_fallible(
|
fn add_user_fallible(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: manage::AddUserFallibleParams,
|
params: manage::AddUserFallibleParams,
|
||||||
mut result: manage::AddUserFallibleResults,
|
mut result: manage::AddUserFallibleResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "addUserFallible").entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let username = pry!(params.get_username());
|
let username = pry!(params.get_username());
|
||||||
let password = pry!(params.get_password());
|
let password = pry!(params.get_password());
|
||||||
// FIXME: saslprep passwords & usernames before storing them
|
// FIXME: saslprep passwords & usernames before storing them
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
params.username = username,
|
||||||
|
params.password = "<redacted>",
|
||||||
|
"method call"
|
||||||
|
);
|
||||||
|
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
|
|
||||||
if !username.is_empty() && !password.is_empty() {
|
if !username.is_empty() && !password.is_empty() {
|
||||||
@ -81,21 +108,29 @@ impl manage::Server for Users {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_user(
|
fn remove_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: manage::RemoveUserParams,
|
params: manage::RemoveUserParams,
|
||||||
_: manage::RemoveUserResults,
|
_: manage::RemoveUserResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "removeUser",).entered();
|
||||||
|
|
||||||
let who: &str = pry!(pry!(pry!(params.get()).get_user()).get_username());
|
let who: &str = pry!(pry!(pry!(params.get()).get_user()).get_username());
|
||||||
|
|
||||||
|
tracing::trace!(params.user = who, "method call");
|
||||||
|
|
||||||
if let Err(e) = self.session.users.del_user(who) {
|
if let Err(e) = self.session.users.del_user(who) {
|
||||||
tracing::warn!("Failed to delete user: {:?}", e);
|
tracing::warn!("Failed to delete user: {:?}", e);
|
||||||
} else {
|
} else {
|
||||||
tracing::info!("Deleted user {}", who);
|
tracing::info!("Deleted user {}", who);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,9 +141,17 @@ impl search::Server for Users {
|
|||||||
params: search::GetUserByNameParams,
|
params: search::GetUserByNameParams,
|
||||||
mut result: search::GetUserByNameResults,
|
mut result: search::GetUserByNameResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserByName",).entered();
|
||||||
|
|
||||||
let username: &str = pry!(pry!(params.get()).get_username());
|
let username: &str = pry!(pry!(params.get()).get_username());
|
||||||
|
|
||||||
|
tracing::trace!(params.username = username, "method call");
|
||||||
|
|
||||||
let userref = UserRef::new(username.to_string());
|
let userref = UserRef::new(username.to_string());
|
||||||
User::build_optional(&self.session, Some(userref), result.get());
|
User::build_optional(&self.session, Some(userref), result.get());
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tracing::Span;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
mod dummy;
|
mod dummy;
|
||||||
@ -33,12 +34,17 @@ pub trait Initiator: Future<Output = ()> {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InitiatorCallbacks {
|
pub struct InitiatorCallbacks {
|
||||||
|
span: Span,
|
||||||
resource: Resource,
|
resource: Resource,
|
||||||
sessions: SessionManager,
|
sessions: SessionManager,
|
||||||
}
|
}
|
||||||
impl InitiatorCallbacks {
|
impl InitiatorCallbacks {
|
||||||
pub fn new(resource: Resource, sessions: SessionManager) -> Self {
|
pub fn new(span: Span, resource: Resource, sessions: SessionManager) -> Self {
|
||||||
Self { resource, sessions }
|
Self {
|
||||||
|
span,
|
||||||
|
resource,
|
||||||
|
sessions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn try_update(&mut self, session: SessionHandle, status: Status) {
|
pub async fn try_update(&mut self, session: SessionHandle, status: Status) {
|
||||||
@ -46,17 +52,19 @@ impl InitiatorCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_session(&self, uid: &str) -> Option<SessionHandle> {
|
pub fn open_session(&self, uid: &str) -> Option<SessionHandle> {
|
||||||
self.sessions.open(uid)
|
self.sessions.open(&self.span, uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InitiatorDriver {
|
pub struct InitiatorDriver {
|
||||||
|
span: Span,
|
||||||
name: String,
|
name: String,
|
||||||
initiator: Box<dyn Initiator + Unpin + Send>,
|
initiator: Box<dyn Initiator + Unpin + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitiatorDriver {
|
impl InitiatorDriver {
|
||||||
pub fn new<I>(
|
pub fn new<I>(
|
||||||
|
span: Span,
|
||||||
name: String,
|
name: String,
|
||||||
params: &HashMap<String, String>,
|
params: &HashMap<String, String>,
|
||||||
resource: Resource,
|
resource: Resource,
|
||||||
@ -65,9 +73,13 @@ impl InitiatorDriver {
|
|||||||
where
|
where
|
||||||
I: 'static + Initiator + Unpin + Send,
|
I: 'static + Initiator + Unpin + Send,
|
||||||
{
|
{
|
||||||
let callbacks = InitiatorCallbacks::new(resource, sessions);
|
let callbacks = InitiatorCallbacks::new(span.clone(), resource, sessions);
|
||||||
let initiator = Box::new(I::new(params, callbacks)?);
|
let initiator = Box::new(I::new(params, callbacks)?);
|
||||||
Ok(Self { name, initiator })
|
Ok(Self {
|
||||||
|
span,
|
||||||
|
name,
|
||||||
|
initiator,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,15 +145,22 @@ fn load_single(
|
|||||||
resource: Resource,
|
resource: Resource,
|
||||||
sessions: &SessionManager,
|
sessions: &SessionManager,
|
||||||
) -> Option<InitiatorDriver> {
|
) -> Option<InitiatorDriver> {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
"initiator",
|
||||||
|
name = %name,
|
||||||
|
module = %module_name,
|
||||||
|
);
|
||||||
tracing::info!(%name, %module_name, ?params, "Loading initiator");
|
tracing::info!(%name, %module_name, ?params, "Loading initiator");
|
||||||
let o = match module_name.as_ref() {
|
let o = match module_name.as_ref() {
|
||||||
"Dummy" => Some(InitiatorDriver::new::<Dummy>(
|
"Dummy" => Some(InitiatorDriver::new::<Dummy>(
|
||||||
|
span,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
params,
|
params,
|
||||||
resource,
|
resource,
|
||||||
sessions.clone(),
|
sessions.clone(),
|
||||||
)),
|
)),
|
||||||
"Process" => Some(InitiatorDriver::new::<Process>(
|
"Process" => Some(InitiatorDriver::new::<Process>(
|
||||||
|
span,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
params,
|
params,
|
||||||
resource,
|
resource,
|
||||||
|
@ -43,7 +43,7 @@ mod tls;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures_util::StreamExt;
|
use futures_util::{FutureExt, StreamExt};
|
||||||
use miette::{Context, IntoDiagnostic, Report};
|
use miette::{Context, IntoDiagnostic, Report};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
@ -94,9 +94,7 @@ impl Diflouroborane {
|
|||||||
}
|
}
|
||||||
tracing::info!("Server is being spawned");
|
tracing::info!("Server is being spawned");
|
||||||
let handle = executor.spawn(server.serve());
|
let handle = executor.spawn(server.serve());
|
||||||
std::thread::spawn(move || {
|
executor.spawn(handle.map(|result| match result {
|
||||||
let result = async_io::block_on(handle);
|
|
||||||
match result {
|
|
||||||
Some(Ok(())) => {
|
Some(Ok(())) => {
|
||||||
tracing::info!("console server finished without error");
|
tracing::info!("console server finished without error");
|
||||||
}
|
}
|
||||||
@ -106,8 +104,7 @@ impl Diflouroborane {
|
|||||||
None => {
|
None => {
|
||||||
tracing::info!("console server finished with panic");
|
tracing::info!("console server finished with panic");
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
let env = StateDB::open_env(&config.db_path)?;
|
let env = StateDB::open_env(&config.db_path)?;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use crate::authorization::roles::Roles;
|
|||||||
use crate::resources::Resource;
|
use crate::resources::Resource;
|
||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
use crate::Users;
|
use crate::Users;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionManager {
|
pub struct SessionManager {
|
||||||
@ -16,11 +17,18 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make infallible
|
// TODO: make infallible
|
||||||
pub fn open(&self, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
pub fn open(&self, parent: &Span, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
||||||
let uid = uid.as_ref();
|
let uid = uid.as_ref();
|
||||||
if let Some(user) = self.users.get_user(uid) {
|
if let Some(user) = self.users.get_user(uid) {
|
||||||
tracing::trace!(uid, ?user, "opening new session for user");
|
let span = tracing::info_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
parent: parent,
|
||||||
|
"session",
|
||||||
|
uid = uid,
|
||||||
|
);
|
||||||
|
tracing::trace!(parent: &span, uid, ?user, "opening session");
|
||||||
Some(SessionHandle {
|
Some(SessionHandle {
|
||||||
|
span,
|
||||||
users: self.users.clone(),
|
users: self.users.clone(),
|
||||||
roles: self.roles.clone(),
|
roles: self.roles.clone(),
|
||||||
user: UserRef::new(user.id),
|
user: UserRef::new(user.id),
|
||||||
@ -33,6 +41,8 @@ impl SessionManager {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionHandle {
|
pub struct SessionHandle {
|
||||||
|
pub span: Span,
|
||||||
|
|
||||||
pub users: Users,
|
pub users: Users,
|
||||||
pub roles: Roles,
|
pub roles: Roles,
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user