use super::{
AccessAction, AccessDecisionError, AccessRefusal, AccessResource, DeliberateDecision,
GrantMode, GrantReceipt, LiveAnswer, LysDeliberateConsumer, LysPass, LysPassAdapter,
RefusalCause, Target, cause_of, live_cause_of,
};
use lys_pass::Error;
impl LysDeliberateConsumer for LysPassAdapter {
fn hold_permission(
&self,
pass: &LysPass,
request: super::super::prepared::PermissionRequest<'_>,
) -> Result<serde_json::Value, Error> {
request.execute(pass)
}
fn hold_site(
&self,
pass: &LysPass,
request: super::super::prepared::SiteRequest<'_>,
) -> Result<serde_json::Value, Error> {
request.execute(pass)
}
fn decide_live(
&self,
pass: &LysPass,
resource: &AccessResource,
action: AccessAction,
now: u64,
) -> Result<DeliberateDecision, AccessDecisionError> {
let refused = |cause| AccessDecisionError {
cause,
refusal: AccessRefusal::new(resource.clone(), action, self.request_url()),
};
let Some(live) = self.live.as_ref() else {
return Err(refused(RefusalCause::DeliberateUnavailable));
};
if pass.verified.claims().exp <= now {
return Err(refused(RefusalCause::Expired));
}
let target = Target::new(resource.kind.qualified(), &resource.id, action.name())
.map_err(|error| refused(cause_of(&error)))?;
let advised = |lacking: &str| {
super::super::advisory::advised_deliberate(
self.decisions,
&pass.warned,
pass.holder(),
resource,
action,
lacking,
)
};
match live.ask(&pass.token, pass.holder(), &target, now) {
Ok(LiveAnswer::Outright { grant }) => {
Ok(DeliberateDecision::Outright(GrantReceipt { grant }))
}
Ok(LiveAnswer::Held {
mode: GrantMode::ByDraft,
grant,
draft,
}) => Ok(advised(&format!("held under draft {draft}"))
.unwrap_or(DeliberateDecision::ByDraft { grant, draft })),
Ok(LiveAnswer::Held {
mode: GrantMode::ByTwo,
grant,
draft,
}) => Ok(advised(&format!("held under draft {draft}"))
.unwrap_or(DeliberateDecision::ByTwo { grant, draft })),
Ok(LiveAnswer::Held {
mode: GrantMode::Outright,
..
}) => Err(refused(RefusalCause::DeliberateUnavailable)),
Ok(LiveAnswer::Refused { refusal }) => {
advised(&refusal).ok_or_else(|| refused(RefusalCause::MissingGrant))
}
Err(error) => {
tracing::warn!(resource = %resource.id, action = action.name(), %error, "a live Lys decision could not be made");
let cause = live_cause_of(&error);
if cause == RefusalCause::DeliberateUnavailable {
advised(error.name()).ok_or_else(|| refused(cause))
} else {
Err(refused(cause))
}
}
}
}
}