use super::{
Arc, AuthConfig, ConnectionNotifier, ConnectionRuntime, ConnectionServices,
ConnectionSupervisor, LimitsConfig, PassVerifier, ServerError, SupervisorInner,
};
pub(super) fn configured_authentication(
auth: &AuthConfig,
) -> Result<(Vec<u8>, Option<PassVerifier>), ServerError> {
let pass_verifier = PassVerifier::from_auth(auth)?;
Ok((auth.token.clone().into_bytes(), pass_verifier))
}
#[derive(Debug)]
pub struct ConnectionSupervisorBuilder {
pub(super) services: Arc<dyn ConnectionServices>,
pub(super) notifier: Option<Arc<dyn ConnectionNotifier>>,
pub(super) auth_token: Option<Vec<u8>>,
pub(super) pass_verifier: Option<PassVerifier>,
pub(super) lys: Option<LysInstall>,
pub(super) limits: LimitsConfig,
}
#[derive(Debug)]
pub struct LysInstall {
pub(crate) admitter: Arc<dyn crate::auth_lys::session::LysAdmitter>,
pub(crate) network_id: String,
pub(crate) link_host: Option<Arc<crate::link::LinkHost>>,
pub(crate) services: Option<Arc<crate::services::production::ServiceBindings>>,
pub(crate) membership: Option<Arc<crate::auth_membership::MembershipGate>>,
}
pub fn lys_ask_runtime(
auth: Option<&AuthConfig>,
) -> Result<Option<tokio::runtime::Runtime>, ServerError> {
if auth.is_none_or(|auth| auth.lys.is_none()) {
return Ok(None);
}
tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.thread_name("liminal-lys-ask")
.enable_all()
.build()
.map(Some)
.map_err(|error| ServerError::ConfigValidation {
message: format!("the Lys ask runtime could not start: {error}"),
})
}
pub fn configured_lys(
auth: &AuthConfig,
ask_handle: Option<tokio::runtime::Handle>,
) -> Result<Option<LysInstall>, ServerError> {
let Some(lys) = auth.lys.as_ref() else {
return Ok(None);
};
let keys_json =
std::fs::read_to_string(&lys.keys_path).map_err(|error| ServerError::ConfigValidation {
message: format!("auth.lys.keys_path {}: {error}", lys.keys_path.display()),
})?;
let mut adapter = crate::auth_lys::lys_pass_adapter::LysPassAdapter::from_keys_json(
&keys_json,
lys.issuer.clone(),
)
.map_err(|error| ServerError::ConfigValidation {
message: format!("auth.lys key set refused: {error}"),
})?;
if lys.live {
let Some(handle) = ask_handle.as_ref() else {
return Err(ServerError::ConfigValidation {
message:
"auth.lys.live needs the server's Lys ask runtime at start; none was given"
.to_owned(),
});
};
let mut live = crate::auth_lys::live::HttpLysAsk::new(
lys.api_url.as_deref().unwrap_or(&lys.issuer),
handle.clone(),
)
.map_err(|error| ServerError::ConfigValidation {
message: format!("auth.lys.issuer refused for live asks: {error}"),
})?;
if let Some(path) = lys.app_credential_path.as_ref() {
let credential =
std::fs::read_to_string(path).map_err(|error| ServerError::ConfigValidation {
message: format!(
"auth.lys.app_credential_path '{}' cannot be read: {}",
path.display(),
error.kind()
),
})?;
live = live.asking_as(credential.trim().to_owned());
}
adapter = adapter.with_live(Arc::new(live));
}
let adapter = Arc::new(adapter);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|_| ServerError::ConfigValidation {
message: "machine pass clock is before the Unix epoch".to_owned(),
})?
.as_secs();
let machine_pass = crate::link_pass::configured(lys, adapter, ask_handle, now)?;
let membership = lys
.membership
.as_ref()
.map(|membership| crate::auth_membership::configured_gate(membership, &lys.issuer))
.transpose()?;
Ok(Some(LysInstall {
admitter: machine_pass.admitter,
network_id: lys.network_id.clone(),
link_host: machine_pass.host,
services: None,
membership,
}))
}
impl ConnectionSupervisorBuilder {
pub fn auth(mut self, auth: &AuthConfig) -> Result<Self, ServerError> {
let (auth_token, pass_verifier) = configured_authentication(auth)?;
self.auth_token = Some(auth_token);
self.pass_verifier = pass_verifier;
self.lys = configured_lys(auth, tokio::runtime::Handle::try_current().ok())?;
Ok(self)
}
#[cfg(test)]
#[must_use]
pub(in crate::server::connection) fn lys_for_tests(mut self, lys: LysInstall) -> Self {
self.lys = Some(lys);
self
}
#[must_use]
pub fn notifier(mut self, notifier: Arc<dyn ConnectionNotifier>) -> Self {
self.notifier = Some(notifier);
self
}
#[must_use]
pub const fn limits(mut self, limits: LimitsConfig) -> Self {
self.limits = limits;
self
}
pub fn build(self) -> Result<ConnectionSupervisor, ServerError> {
SupervisorInner::new(
self.services,
self.notifier,
self.auth_token,
self.pass_verifier,
self.lys,
self.limits,
None,
)
.map(|inner| ConnectionSupervisor {
inner: Arc::new(inner),
})
}
}
pub(super) fn install_lys(runtime: &ConnectionRuntime, lys: LysInstall) -> Result<(), ServerError> {
let LysInstall {
admitter,
network_id,
link_host,
services: bindings,
membership,
} = lys;
runtime
.install_lys_admitter(admitter, network_id)
.map_err(|_| ServerError::ConfigValidation {
message: "auth.lys admitter was already installed".to_owned(),
})?;
if let Some(host) = link_host {
runtime
.install_link_host(host)
.map_err(|_| ServerError::ConfigValidation {
message: "auth.lys.machine was already installed".to_owned(),
})?;
}
if let Some(gate) = membership {
runtime
.install_membership_gate(gate)
.map_err(|_| ServerError::ConfigValidation {
message: "auth.lys.membership was already installed".to_owned(),
})?;
}
if let Some(bindings) = bindings {
runtime
.install_service_bindings(bindings)
.map_err(|_| ServerError::ConfigValidation {
message: "auth.lys.services was already installed".to_owned(),
})?;
}
Ok(())
}