liminal_server/services/
production.rs1use super::actions::PublishingActions;
21use super::budgets::LysBudgets;
22use super::context_watch::ContextReporter;
23use super::keys::{ChannelKeySender, ConnectionPublisher, ServicePublisher};
24use super::lys_http::{HttpLys, LysHttp};
25use super::seats::LysSeatStarter;
26use super::{ServiceEffectError, Services};
27use crate::auth_lys::LysPassConsumer;
28use crate::config::types::LysConfig;
29use crate::server::connection::LiminalConnectionServices;
30use std::sync::Arc;
31
32pub struct ServiceBindings {
34 seats: LysSeatStarter,
35 keys: ChannelKeySender,
36 budgets: LysBudgets,
37 actions: PublishingActions,
38 context: Arc<ContextReporter>,
39 watch_context: bool,
40}
41
42impl std::fmt::Debug for ServiceBindings {
43 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 formatter
45 .debug_struct("ServiceBindings")
46 .field("seats", &self.seats)
47 .field("keys", &self.keys)
48 .field("budgets", &self.budgets)
49 .field("actions", &self.actions)
50 .field("watch_context", &self.watch_context)
51 .finish_non_exhaustive()
52 }
53}
54
55impl ServiceBindings {
56 #[must_use]
60 pub fn new(
61 http: &Arc<dyn LysHttp>,
62 bearer: &str,
63 machine: String,
64 publisher: &Arc<dyn ServicePublisher>,
65 ) -> Self {
66 let context = Arc::new(ContextReporter::new(Arc::clone(http), bearer.to_owned()));
67 Self {
68 seats: LysSeatStarter::new(Arc::clone(http), bearer.to_owned(), machine),
69 keys: ChannelKeySender::new(Arc::clone(publisher)),
70 budgets: LysBudgets::new(Arc::clone(http), bearer.to_owned()),
71 actions: PublishingActions::new(Arc::clone(publisher), Arc::clone(&context)),
72 context,
73 watch_context: false,
74 }
75 }
76
77 #[must_use]
79 pub const fn watches_context(&self) -> bool {
80 self.watch_context
81 }
82
83 #[must_use]
85 pub fn services(&self) -> Services<'_> {
86 Services {
87 seats: &self.seats,
88 keys: &self.keys,
89 budgets: &self.budgets,
90 actions: &self.actions,
91 }
92 }
93
94 #[must_use]
96 pub fn context_reporter(&self) -> Arc<ContextReporter> {
97 Arc::clone(&self.context)
98 }
99
100 pub fn configure(
109 lys: &LysConfig,
110 services: Arc<LiminalConnectionServices>,
111 handle: tokio::runtime::Handle,
112 now: u64,
113 ) -> Result<Option<Self>, ServiceEffectError> {
114 let Some(section) = lys.services.as_ref() else {
115 return Ok(None);
116 };
117 let bearer = machine_bearer(lys, now)?;
118 let http: Arc<dyn LysHttp> = Arc::new(HttpLys::new(
119 lys.api_url.as_deref().unwrap_or(&lys.issuer),
120 handle,
121 )?);
122 let publisher: Arc<dyn ServicePublisher> =
123 Arc::new(ConnectionPublisher::new(services, section.host.clone()));
124 let mut bindings = Self::new(&http, &bearer, section.machine.clone(), &publisher);
125 bindings.watch_context = section.context_watch;
126 Ok(Some(bindings))
127 }
128}
129
130pub fn machine_bearer(lys: &LysConfig, now: u64) -> Result<String, ServiceEffectError> {
139 let machine = lys.machine.as_ref().ok_or_else(|| {
140 ServiceEffectError(
141 "auth.lys.services needs auth.lys.machine: the services ask Lys as this machine"
142 .to_owned(),
143 )
144 })?;
145 let keys_json = std::fs::read_to_string(&lys.keys_path).map_err(|error| {
146 ServiceEffectError(format!(
147 "auth.lys.keys_path {}: {error}",
148 lys.keys_path.display()
149 ))
150 })?;
151 let adapter = crate::auth_lys::lys_pass_adapter::LysPassAdapter::from_keys_json(
152 &keys_json,
153 lys.issuer.clone(),
154 )
155 .map_err(|error| ServiceEffectError(format!("auth.lys key set refused: {error}")))?;
156 let pass_bytes = std::fs::read(&machine.pass_path).map_err(|error| {
157 ServiceEffectError(format!(
158 "auth.lys.machine.pass_path {}: {error}",
159 machine.pass_path.display()
160 ))
161 })?;
162 adapter
163 .verify_offline(&pass_bytes, crate::auth_lys::LIMINAL_PASS_AUDIENCE, now)
164 .map_err(|error| ServiceEffectError(format!("auth.lys.machine pass refused: {error}")))?;
165 let token = std::str::from_utf8(&pass_bytes)
166 .map_err(|_| ServiceEffectError("auth.lys.machine pass is not UTF-8".to_owned()))?;
167 Ok(token.trim().to_owned())
168}