use super::{
AccessAction, AccessDecisionError, AccessResource, AdmittedLysPass, DeliberateDecision,
GrantReceipt, HotAction, LysDeliberateConsumer, LysPassConsumer, SubjectBinding,
admit_lys_pass, decide_deliberate, decide_message,
};
use crate::link::LinkAction;
use std::sync::Arc;
impl SubjectBinding {
pub fn link(
network_id: String,
link_id: String,
subject: String,
) -> Result<Self, super::SubjectBindingError> {
let mut binding = Self::new(network_id, link_id, subject)?;
binding.resource.kind = super::ResourceKind::Link;
Ok(binding)
}
}
#[derive(Debug)]
pub struct LysAdmissionRefusal {
pub reason: String,
}
impl std::fmt::Display for LysAdmissionRefusal {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "lys pass admission refused: {}", self.reason)
}
}
impl std::error::Error for LysAdmissionRefusal {}
pub trait LysAdmitter: Send + Sync + std::fmt::Debug {
fn admit(&self, pass: &[u8], now: u64) -> Result<Box<dyn LysSession>, LysAdmissionRefusal>;
}
pub trait LysSession: Send + Sync + std::fmt::Debug {
fn hold_permission(
&self,
_request: super::prepared::PermissionRequest<'_>,
) -> Result<serde_json::Value, lys_pass::Error> {
Err(lys_pass::Error::CannotAsk(
"prepared permission unavailable",
))
}
fn hold_site(
&self,
_request: super::prepared::SiteRequest<'_>,
) -> Result<serde_json::Value, lys_pass::Error> {
Err(lys_pass::Error::CannotAsk(
"prepared construction unavailable",
))
}
fn holder(&self) -> &str;
fn holder_kind(&self) -> Option<&str> {
None
}
fn issued_at(&self) -> Option<u64> {
None
}
fn decide(
&self,
subject: &SubjectBinding,
action: HotAction,
now: u64,
) -> Result<GrantReceipt, AccessDecisionError>;
fn decide_link(
&self,
subject: &SubjectBinding,
action: LinkAction,
now: u64,
) -> Result<GrantReceipt, AccessDecisionError>;
fn open_link(
&self,
link: &AccessResource,
end: LinkAction,
now: u64,
) -> Result<DeliberateDecision, AccessDecisionError>;
fn decide_live(
&self,
resource: &AccessResource,
action: AccessAction,
now: u64,
) -> Result<DeliberateDecision, AccessDecisionError>;
}
#[derive(Debug)]
pub struct ConsumerAdmitter<C> {
consumer: Arc<C>,
}
impl<C> ConsumerAdmitter<C> {
#[must_use]
pub const fn new(consumer: Arc<C>) -> Self {
Self { consumer }
}
}
struct ConsumerSession<C: LysPassConsumer> {
consumer: Arc<C>,
pass: AdmittedLysPass<C::Pass>,
holder: String,
holder_kind: Option<String>,
}
impl<C: LysPassConsumer> std::fmt::Debug for ConsumerSession<C> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ConsumerSession")
.field("holder", &self.holder)
.finish_non_exhaustive()
}
}
impl<C> LysAdmitter for ConsumerAdmitter<C>
where
C: LysDeliberateConsumer + Send + Sync + std::fmt::Debug + 'static,
C::Pass: Send + Sync + 'static,
C::Holder: AsRef<str>,
{
fn admit(&self, pass: &[u8], now: u64) -> Result<Box<dyn LysSession>, LysAdmissionRefusal> {
let admitted = admit_lys_pass(self.consumer.as_ref(), pass, now).map_err(|error| {
LysAdmissionRefusal {
reason: error.to_string(),
}
})?;
let holder = self
.consumer
.verified_holder(&admitted.pass)
.as_ref()
.to_owned();
let holder_kind = self
.consumer
.verified_holder_kind(&admitted.pass)
.map(str::to_owned);
Ok(Box::new(ConsumerSession {
consumer: Arc::clone(&self.consumer),
pass: admitted,
holder,
holder_kind,
}))
}
}
impl<C> LysSession for ConsumerSession<C>
where
C: LysDeliberateConsumer + Send + Sync + 'static,
C::Pass: Send + Sync + 'static,
{
fn hold_permission(
&self,
request: super::prepared::PermissionRequest<'_>,
) -> Result<serde_json::Value, lys_pass::Error> {
self.consumer.hold_permission(&self.pass.pass, request)
}
fn hold_site(
&self,
request: super::prepared::SiteRequest<'_>,
) -> Result<serde_json::Value, lys_pass::Error> {
self.consumer.hold_site(&self.pass.pass, request)
}
fn holder(&self) -> &str {
&self.holder
}
fn holder_kind(&self) -> Option<&str> {
self.holder_kind.as_deref()
}
fn issued_at(&self) -> Option<u64> {
self.consumer.verified_issued_at(&self.pass.pass)
}
fn decide(
&self,
subject: &SubjectBinding,
action: HotAction,
now: u64,
) -> Result<GrantReceipt, AccessDecisionError> {
decide_message(self.consumer.as_ref(), &self.pass, subject, action, now)
}
fn decide_link(
&self,
subject: &SubjectBinding,
action: LinkAction,
now: u64,
) -> Result<GrantReceipt, AccessDecisionError> {
self.consumer
.decide_link(&self.pass.pass, subject, action, now)
}
fn open_link(
&self,
link: &AccessResource,
end: LinkAction,
now: u64,
) -> Result<DeliberateDecision, AccessDecisionError> {
self.decide_live(link, end.access_action(), now)
}
fn decide_live(
&self,
resource: &AccessResource,
action: AccessAction,
now: u64,
) -> Result<DeliberateDecision, AccessDecisionError> {
decide_deliberate(self.consumer.as_ref(), &self.pass, resource, action, now)
}
}