liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use super::{Authority, MachineConfig, PassRefusal};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex, Weak};
use zeroize::Zeroizing;

pub(super) struct HeldToken {
    pub(super) token: Zeroizing<String>,
    authority: Weak<Authority>,
}
type PassCacheToken = Mutex<HeldToken>;
type PassCache = BTreeMap<Vec<u8>, Weak<PassCacheToken>>;
static PASSES: Mutex<PassCache> = Mutex::new(BTreeMap::new());
#[derive(Clone)]
pub struct MachinePass(pub(super) Arc<PassCacheToken>);
impl std::fmt::Debug for MachinePass {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MachinePass").finish_non_exhaustive()
    }
}
impl MachinePass {
    pub(crate) fn configured(machine: &MachineConfig) -> Result<Self, PassRefusal> {
        Self::hold(machine, None)
    }
    pub(super) fn hold(
        machine: &MachineConfig,
        token: Option<Zeroizing<String>>,
    ) -> Result<Self, PassRefusal> {
        let source = serde_json::to_vec(&(
            &machine.identity_path,
            &machine.pass_path,
            &machine.agent_id,
            &machine.audience,
        ))
        .map_err(|_| PassRefusal("machine_pass_source_refused"))?;
        let mut passes = PASSES
            .lock()
            .map_err(|_| PassRefusal("pass_holder_refused"))?;
        passes.retain(|_, pass| pass.strong_count() > 0);
        if let Some(pass) = passes.get(&source).and_then(Weak::upgrade) {
            return Ok(Self(pass));
        }
        let token = match token {
            Some(token) => token,
            None if machine.agent_id.is_empty() && !machine.pass_path.as_os_str().is_empty() => {
                Zeroizing::new(
                    std::fs::read_to_string(&machine.pass_path)
                        .map_err(|_| PassRefusal("machine_pass_path_refused"))?,
                )
            }
            // A dial whose ask was refused holds nothing until the next open
            // asks again (ACCESS-009 R1).
            None if !machine.agent_id.is_empty() => Zeroizing::new(String::new()),
            None => return Err(PassRefusal("machine_pass_not_configured")),
        };
        let pass = Arc::new(Mutex::new(HeldToken {
            token: Zeroizing::new(token.trim().to_owned()),
            authority: Weak::new(),
        }));
        passes.insert(source, Arc::downgrade(&pass));
        drop(passes);
        Ok(Self(pass))
    }
    pub(super) fn attach(&self, authority: &Arc<Authority>) -> Result<(), PassRefusal> {
        let mut held = self
            .0
            .lock()
            .map_err(|_| PassRefusal("pass_holder_refused"))?;
        if held.authority.upgrade().is_some() {
            return Err(PassRefusal("machine_pass_already_installed"));
        }
        held.authority = Arc::downgrade(authority);
        drop(held);
        Ok(())
    }
    pub(crate) fn for_open(&self) -> Result<Zeroizing<String>, PassRefusal> {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map_err(|_| PassRefusal("machine_pass_clock_refused"))?
            .as_secs();
        self.for_open_at(now)
    }
    pub(super) fn for_open_at(&self, now: u64) -> Result<Zeroizing<String>, PassRefusal> {
        let authority = self
            .0
            .lock()
            .map_err(|_| PassRefusal("pass_holder_refused"))?
            .authority
            .upgrade();
        if let Some(authority) = authority {
            authority.refresh_expired(now)?;
        }
        self.current()
    }
    /// Whether a pass is held now: false after a boot whose ask Lys
    /// refused, until an open asks again (ACCESS-009 R1).
    pub(crate) fn issued(&self) -> Result<bool, PassRefusal> {
        self.0
            .lock()
            .map(|held| !held.token.is_empty())
            .map_err(|_| PassRefusal("pass_holder_refused"))
    }
    pub(crate) fn current(&self) -> Result<Zeroizing<String>, PassRefusal> {
        self.0
            .lock()
            .map(|held| held.token.clone())
            .map_err(|_| PassRefusal("pass_holder_refused"))
    }
}