liminal_server/server/connection/supervisor/
builder.rs1use super::{
2 Arc, AuthConfig, ConnectionNotifier, ConnectionRuntime, ConnectionServices,
3 ConnectionSupervisor, LimitsConfig, PassVerifier, ServerError, SupervisorInner,
4};
5
6pub(super) fn configured_authentication(
12 auth: &AuthConfig,
13) -> Result<(Vec<u8>, Option<PassVerifier>), ServerError> {
14 let pass_verifier = PassVerifier::from_auth(auth)?;
15 Ok((auth.token.clone().into_bytes(), pass_verifier))
16}
17
18pub struct ConnectionSupervisorBuilder {
24 pub(super) services: Arc<dyn ConnectionServices>,
25 pub(super) notifier: Option<Arc<dyn ConnectionNotifier>>,
26 pub(super) auth_token: Option<Vec<u8>>,
27 pub(super) pass_verifier: Option<PassVerifier>,
28 pub(super) lys: Option<LysInstall>,
29 pub(super) limits: LimitsConfig,
30}
31
32impl std::fmt::Debug for ConnectionSupervisorBuilder {
34 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 formatter
36 .debug_struct("ConnectionSupervisorBuilder")
37 .field("services", &self.services)
38 .field("notifier", &self.notifier)
39 .field("auth_token_len", &self.auth_token.as_ref().map(Vec::len))
40 .field("pass_verifier", &self.pass_verifier)
41 .field("lys", &self.lys)
42 .field("limits", &self.limits)
43 .finish()
44 }
45}
46
47#[derive(Debug)]
52pub struct LysInstall {
53 pub(crate) admitter: Arc<dyn crate::auth_lys::session::LysAdmitter>,
55 pub(crate) network_id: String,
57 pub(crate) link_host: Option<Arc<crate::link::LinkHost>>,
59 pub(crate) services: Option<Arc<crate::services::production::ServiceBindings>>,
61 pub(crate) membership: Option<Arc<crate::auth_membership::MembershipGate>>,
64}
65
66pub fn lys_ask_runtime(
75 auth: Option<&AuthConfig>,
76) -> Result<Option<tokio::runtime::Runtime>, ServerError> {
77 if auth.is_none_or(|auth| auth.lys.is_none()) {
78 return Ok(None);
79 }
80 tokio::runtime::Builder::new_multi_thread()
81 .worker_threads(1)
82 .thread_name("liminal-lys-ask")
83 .enable_all()
84 .build()
85 .map(Some)
86 .map_err(|error| ServerError::ConfigValidation {
87 message: format!("the Lys ask runtime could not start: {error}"),
88 })
89}
90
91pub fn configured_lys(
99 auth: &AuthConfig,
100 ask_handle: Option<tokio::runtime::Handle>,
101) -> Result<Option<LysInstall>, ServerError> {
102 Ok(lys_install(auth, ask_handle, false)?.map(|(install, _)| install))
103}
104
105pub fn configured_lys_after_bind(
112 auth: &AuthConfig,
113 ask_handle: Option<tokio::runtime::Handle>,
114) -> Result<(Option<LysInstall>, Option<crate::link_pass::StartIssue>), ServerError> {
115 Ok(lys_install(auth, ask_handle, true)?
116 .map_or((None, None), |(install, issue)| (Some(install), issue)))
117}
118
119fn lys_install(
120 auth: &AuthConfig,
121 ask_handle: Option<tokio::runtime::Handle>,
122 after_bind: bool,
123) -> Result<Option<(LysInstall, Option<crate::link_pass::StartIssue>)>, ServerError> {
124 let Some(lys) = auth.lys.as_ref() else {
125 return Ok(None);
126 };
127 let keys_json =
128 std::fs::read_to_string(&lys.keys_path).map_err(|error| ServerError::ConfigValidation {
129 message: format!("auth.lys.keys_path {}: {error}", lys.keys_path.display()),
130 })?;
131 let mut adapter = crate::auth_lys::lys_pass_adapter::LysPassAdapter::from_keys_json(
132 &keys_json,
133 lys.issuer.clone(),
134 )
135 .map_err(|error| ServerError::ConfigValidation {
136 message: format!("auth.lys key set refused: {error}"),
137 })?
138 .with_decisions(lys.decisions);
139 if lys.live {
140 let Some(handle) = ask_handle.as_ref() else {
143 return Err(ServerError::ConfigValidation {
144 message:
145 "auth.lys.live needs the server's Lys ask runtime at start; none was given"
146 .to_owned(),
147 });
148 };
149 let mut live = crate::auth_lys::live::HttpLysAsk::new(
150 lys.api_url.as_deref().unwrap_or(&lys.issuer),
151 handle.clone(),
152 )
153 .map_err(|error| ServerError::ConfigValidation {
154 message: format!("auth.lys.issuer refused for live asks: {error}"),
155 })?;
156 if let Some(path) = lys.app_credential_path.as_ref() {
160 let credential =
161 std::fs::read_to_string(path).map_err(|error| ServerError::ConfigValidation {
162 message: format!(
163 "auth.lys.app_credential_path '{}' cannot be read: {}",
164 path.display(),
165 error.kind()
166 ),
167 })?;
168 live = live.asking_as(credential.trim().to_owned());
169 }
170 adapter = adapter.with_live(Arc::new(live));
171 }
172 let adapter = Arc::new(adapter);
173 let now = std::time::SystemTime::now()
174 .duration_since(std::time::UNIX_EPOCH)
175 .map_err(|_| ServerError::ConfigValidation {
176 message: "machine pass clock is before the Unix epoch".to_owned(),
177 })?
178 .as_secs();
179 let machine_pass = crate::link_pass::configured(lys, adapter, ask_handle, now, after_bind)?;
180 let membership = lys
181 .membership
182 .as_ref()
183 .map(|membership| crate::auth_membership::configured_gate(membership, &lys.issuer))
184 .transpose()?;
185 Ok(Some((
186 LysInstall {
187 admitter: machine_pass.admitter,
188 network_id: lys.network_id.clone(),
189 link_host: machine_pass.host,
190 services: None,
191 membership,
192 },
193 machine_pass.issue,
194 )))
195}
196
197impl ConnectionSupervisorBuilder {
198 pub fn auth(mut self, auth: &AuthConfig) -> Result<Self, ServerError> {
208 let (auth_token, pass_verifier) = configured_authentication(auth)?;
209 self.auth_token = Some(auth_token);
210 self.pass_verifier = pass_verifier;
211 self.lys = configured_lys(auth, tokio::runtime::Handle::try_current().ok())?;
212 Ok(self)
213 }
214
215 #[cfg(test)]
218 #[must_use]
219 pub(in crate::server::connection) fn lys_for_tests(mut self, lys: LysInstall) -> Self {
220 self.lys = Some(lys);
221 self
222 }
223
224 #[must_use]
227 pub fn notifier(mut self, notifier: Arc<dyn ConnectionNotifier>) -> Self {
228 self.notifier = Some(notifier);
229 self
230 }
231
232 #[must_use]
234 pub const fn limits(mut self, limits: LimitsConfig) -> Self {
235 self.limits = limits;
236 self
237 }
238
239 pub fn build(self) -> Result<ConnectionSupervisor, ServerError> {
245 SupervisorInner::new(
246 self.services,
247 self.notifier,
248 self.auth_token,
249 self.pass_verifier,
250 self.lys,
251 self.limits,
252 None,
253 )
254 .map(|inner| ConnectionSupervisor {
255 inner: Arc::new(inner),
256 })
257 }
258}
259
260pub(super) fn install_lys(runtime: &ConnectionRuntime, lys: LysInstall) -> Result<(), ServerError> {
263 let LysInstall {
264 admitter,
265 network_id,
266 link_host,
267 services: bindings,
268 membership,
269 } = lys;
270 runtime
271 .install_lys_admitter(admitter, network_id)
272 .map_err(|_| ServerError::ConfigValidation {
273 message: "auth.lys admitter was already installed".to_owned(),
274 })?;
275 if let Some(host) = link_host {
276 runtime
277 .install_link_host(host)
278 .map_err(|_| ServerError::ConfigValidation {
279 message: "auth.lys.machine was already installed".to_owned(),
280 })?;
281 }
282 if let Some(gate) = membership {
283 runtime
284 .install_membership_gate(gate)
285 .map_err(|_| ServerError::ConfigValidation {
286 message: "auth.lys.membership was already installed".to_owned(),
287 })?;
288 }
289 if let Some(bindings) = bindings {
290 runtime
291 .install_service_bindings(bindings)
292 .map_err(|_| ServerError::ConfigValidation {
293 message: "auth.lys.services was already installed".to_owned(),
294 })?;
295 }
296 Ok(())
297}