1use core::fmt::Debug;
3use std::ops::{Deref, DerefMut};
4use std::path::{Path, PathBuf};
5
6use anyhow::{Context as _, Result};
7use url::Url;
8
9use crate::misc::serde_ext::bytes_wrapper::{B64, B64UU};
10use crate::servers::{for_all_servers, server::Server as _};
11use crate::{
12 api::{self},
13 attr,
14 common::{elgamal, kem},
15 hub,
16 misc::{jwt, serde_ext, time_ext},
17 servers::yivi,
18};
19
20use super::host_aliases::{HostAliases, UrlPwa};
21use super::log::LogConfig;
22
23#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
27#[serde(deny_unknown_fields)]
28pub struct Config {
29 pub phc_url: UrlPwa,
33
34 #[serde(default)]
36 pub log: Option<LogConfig>,
37
38 #[serde(skip)]
39 pub(crate) preparation_state: PreparationState,
40
41 #[serde(default)]
46 pub host_aliases: HostAliases,
47
48 #[serde(default)]
50 pub wd: PathBuf,
51
52 pub phc: Option<ServerConfig<phc::ExtraConfig>>,
54
55 pub transcryptor: Option<ServerConfig<transcryptor::ExtraConfig>>,
57
58 pub auths: Option<ServerConfig<auths::ExtraConfig>>,
60}
61
62#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
64pub(crate) enum PreparationState {
65 #[default]
67 Unprepared,
68
69 Preliminary,
71
72 Complete,
74}
75
76#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
78#[serde(deny_unknown_fields)]
79pub struct ServerConfig<ServerSpecific> {
80 #[serde(default)]
82 pub port: u16,
83
84 #[serde(default = "default_ips")]
86 pub ips: Box<[std::net::IpAddr]>,
87
88 #[serde(default, skip_serializing)]
92 pub bind_to: serde::de::IgnoredAny,
93
94 pub self_check_code: Option<String>,
97
98 pub signing_key: Option<api::SigningKeyBytes>,
103
104 #[serde(default, skip_serializing)]
108 pub jwt_key: serde::de::IgnoredAny,
109
110 pub enc_key: Option<B64>,
118
119 pub admin_key: Option<serde_ext::bytes_wrapper::B16>,
123
124 pub object_store: Option<ObjectStoreConfig>,
126
127 #[serde(default = "default_version")]
131 pub version: Option<String>,
132
133 #[serde(flatten)]
134 extra: ServerSpecific,
136}
137
138impl<X> Deref for ServerConfig<X> {
139 type Target = X;
140
141 fn deref(&self) -> &X {
142 &self.extra
143 }
144}
145
146impl<X> DerefMut for ServerConfig<X> {
147 fn deref_mut(&mut self) -> &mut X {
148 &mut self.extra
149 }
150}
151
152fn default_version() -> Option<String> {
153 crate::servers::version().map(str::to_string)
154}
155
156fn default_ips() -> Box<[std::net::IpAddr]> {
157 Box::new([
158 std::net::Ipv6Addr::UNSPECIFIED.into(), std::net::Ipv4Addr::UNSPECIFIED.into(), ])
163}
164
165impl<'a, X> std::net::ToSocketAddrs for &'a ServerConfig<X> {
166 type Iter = Box<dyn Iterator<Item = std::net::SocketAddr> + 'a>;
167
168 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
169 Ok(Box::new(self.ips.iter().map(|ip| (*ip, self.port).into())))
170 }
171}
172
173impl Config {
174 pub fn load_from_path(path: &Path) -> Result<Option<Self>> {
178 let mut res: Self = toml::from_str(&match std::fs::read_to_string(path) {
180 Ok(contents) => contents,
181 Err(e) => match e.kind() {
182 std::io::ErrorKind::NotFound => return Ok(None),
183 _ => {
184 return Err(e)
185 .with_context(|| format!("could not open config file {}", path.display()));
186 }
187 },
188 })
189 .with_context(|| format!("could not parse config file {}", path.display()))?;
190
191 if res.wd.as_os_str().is_empty() {
192 res.wd = path
193 .canonicalize()
194 .with_context(|| format!("failed to canonicalize path {}", path.display()))?
195 .parent()
196 .expect("did not expect a configuration file without a parent directory")
197 .into();
198 }
199
200 if !res.wd.is_absolute() {
201 anyhow::bail!(
202 "if you specify a working directory (`wd` in {}) it must be absolute",
203 path.display()
204 );
205 }
206
207 res.preliminary_prep()?;
208
209 log::info!(
210 "loaded config file from {}; interpretting relative paths in {}",
211 path.display(),
212 res.wd.display()
213 );
214
215 Ok(Some(res))
216 }
217
218 pub fn preliminary_prep(&mut self) -> Result<()> {
219 anyhow::ensure!(
220 self.preparation_state == PreparationState::Unprepared,
221 "configuration already (partially) prepared: {:?}",
222 self.preparation_state
223 );
224
225 self.host_aliases.resolve_all()?;
226 self.host_aliases.dealias(&mut self.phc_url);
227
228 self.preparation_state = PreparationState::Preliminary;
229
230 Ok(())
231 }
232
233 pub async fn prepare_for(&self, server: crate::servers::Name) -> Result<Self> {
236 anyhow::ensure!(
237 self.preparation_state == PreparationState::Preliminary,
238 "configuration not in the correct preparation state"
239 );
240
241 let Self {
243 log: _,
244 host_aliases,
245 phc_url,
246 wd,
247 preparation_state,
248 phc: _,
249 transcryptor: _,
250 auths: _,
251 } = self;
252
253 let mut config: Config = Config {
254 log: None, host_aliases: host_aliases.clone(),
256 phc_url: phc_url.clone(),
257 wd: wd.clone(),
258 preparation_state: *preparation_state,
259 phc: None,
260 transcryptor: None,
261 auths: None,
262 };
263
264 macro_rules! clone_only_server {
265 ($server:ident) => {
266 if crate::servers::$server::Server::NAME == server {
267 assert!(self.$server.is_some());
268 config.$server.clone_from(&self.$server);
269 }
270 };
271 }
272
273 for_all_servers!(clone_only_server);
274
275 config.prepare().await?;
276
277 Ok(config)
278 }
279
280 pub async fn prepare(&mut self) -> anyhow::Result<()> {
282 let pcc = Pcc::new(actix_web::dev::Extensions::new());
283
284 PrepareConfig::prepare(self, pcc).await
285 }
286
287 pub fn json_updated(&self, pointer: &str, new_value: serde_json::Value) -> Result<Self> {
289 let mut json_config: serde_json::Value =
290 serde_json::to_value(self).context("failed to serialize config")?;
291
292 let to_be_modified: &mut serde_json::Value =
293 json_config.pointer_mut(pointer).with_context(|| {
294 format!(
295 "wanted to modify {pointer} of the configuration file, but that points nowhere"
296 )
297 })?;
298
299 to_be_modified.clone_from(&new_value);
300
301 let new_config: Config = serde_json::from_value(json_config).with_context(|| {
302 format!(
303 "wanted to change {pointer} of the configuration file to {new_value}, but the new configuration did not deserialize",
304 )
305 })?;
306
307 Ok(new_config)
308 }
309}
310
311#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
312pub struct ObjectStoreConfig {
313 pub url: UrlPwa,
320
321 #[serde(default)]
323 pub options: std::collections::HashMap<String, String>,
324}
325
326impl Default for ObjectStoreConfig {
327 fn default() -> Self {
328 Self {
329 url: From::<Url>::from("memory:///".try_into().unwrap()),
330 options: Default::default(),
331 }
332 }
333}
334
335pub mod phc {
336 use super::*;
337
338 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
339 #[serde(deny_unknown_fields)]
340 pub struct ExtraConfig {
341 pub transcryptor_url: UrlPwa,
343
344 pub auths_url: UrlPwa,
346
347 pub global_client_url: UrlPwa,
352
353 pub hubs: Vec<hub::BasicInfo<UrlPwa>>,
355
356 pub master_enc_key_part: Option<elgamal::PrivateKey>,
360
361 pub attr_id_secret: Option<B64UU>,
367
368 #[serde(with = "time_ext::human_duration")]
375 #[serde(default = "default_auth_token_validity")]
376 pub auth_token_validity: core::time::Duration,
377
378 #[serde(with = "time_ext::human_duration")]
381 #[serde(default = "default_auth_token_max_idle")]
382 pub auth_token_max_idle: core::time::Duration,
383
384 #[serde(with = "time_ext::human_duration")]
389 #[serde(default = "default_auth_session_max_duration")]
390 pub auth_session_max_duration: core::time::Duration,
391
392 #[serde(with = "time_ext::human_duration")]
394 #[serde(default = "default_pp_nonce_validity")]
395 pub pp_nonce_validity: core::time::Duration,
396
397 #[serde(with = "time_ext::human_duration")]
400 #[serde(default = "default_card_pseud_validity")]
401 pub card_pseud_validity: core::time::Duration,
402
403 pub user_object_hmac_secret: Option<B64UU>,
407
408 #[serde(default)]
410 pub user_quota: api::phc::user::Quota,
411
412 #[serde(default, skip_serializing)]
414 pub card: serde::de::IgnoredAny,
415
416 #[serde(default)]
417 pub hub_cache: crate::servers::phc::HubCacheConfig,
418 }
419
420 fn default_auth_token_validity() -> core::time::Duration {
421 core::time::Duration::from_secs(60 * 60) }
424
425 fn default_auth_token_max_idle() -> core::time::Duration {
426 core::time::Duration::from_secs(90 * 24 * 60 * 60) }
428
429 fn default_auth_session_max_duration() -> core::time::Duration {
430 core::time::Duration::from_secs(365 * 24 * 60 * 60) }
432
433 fn default_pp_nonce_validity() -> core::time::Duration {
434 core::time::Duration::from_secs(30)
435 }
437
438 fn default_card_pseud_validity() -> core::time::Duration {
439 core::time::Duration::from_secs(30)
440 }
442}
443
444pub mod transcryptor {
445 use super::*;
446
447 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
448 #[serde(deny_unknown_fields)]
449 pub struct ExtraConfig {
450 pub master_enc_key_part: Option<elgamal::PrivateKey>,
454
455 pub pseud_factor_secret: Option<B64UU>,
461
462 pub decap_key: Option<kem::DecapKeyBytes>,
467 }
468}
469
470pub mod auths {
471 use super::*;
472
473 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
474 #[serde(deny_unknown_fields)]
475 pub struct ExtraConfig {
476 #[serde(default)]
477 pub attribute_types: Vec<attr::Type>,
478
479 pub yivi: Option<YiviConfig>,
481
482 #[serde(with = "time_ext::human_duration")]
485 #[serde(default = "default_auth_window")]
486 pub auth_window: core::time::Duration,
487
488 #[serde(default = "default_max_attr_types_per_req")]
494 pub max_attr_types_per_req: usize,
495
496 pub attr_key_secret: Option<B64UU>,
501
502 pub decap_key: Option<kem::DecapKeyBytes>,
507 }
508
509 fn default_auth_window() -> core::time::Duration {
510 core::time::Duration::from_secs(60 * 60) }
513
514 fn default_max_attr_types_per_req() -> usize {
515 4
516 }
517
518 impl ExtraConfig {
519 pub(super) fn filter_attribute_types(&mut self) {
524 let mut supported_sources: std::collections::HashSet<attr::Source> = Default::default();
525
526 if self.yivi.is_some() {
527 assert!(supported_sources.insert(attr::Source::Yivi));
528 }
529
530 for attr_type in self.attribute_types.iter_mut() {
531 attr_type.filter_sources(|s| supported_sources.contains(&s))
532 }
533 }
534 }
535
536 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
537 #[serde(deny_unknown_fields)]
538 pub struct YiviConfig {
539 pub requestor_url: UrlPwa,
542
543 pub requestor_creds: yivi::Credentials<yivi::SigningKey>,
544
545 pub server_name: String,
547
548 pub server_key: Option<yivi::VerifyingKey>,
551
552 #[serde(default)]
555 pub chained_sessions: crate::servers::auths::yivi::ChainedSessionsConfig,
556
557 #[serde(default)]
559 pub card: crate::servers::auths::card::CardConfig,
560 }
561
562 impl YiviConfig {
563 pub fn server_creds(&self) -> yivi::Credentials<yivi::VerifyingKey> {
564 yivi::Credentials {
565 name: self.server_name.clone(),
566 key: self
567 .server_key
568 .clone()
569 .expect("bug: YiviConfig was not properly prepared"),
570 }
571 }
572 }
573}
574
575trait PrepareConfig<C> {
578 async fn prepare(&mut self, context: C) -> anyhow::Result<()>;
579}
580
581type Pcc = std::rc::Rc<actix_web::dev::Extensions>;
582
583impl PrepareConfig<Pcc> for Config {
584 async fn prepare(&mut self, mut c: Pcc) -> anyhow::Result<()> {
585 anyhow::ensure!(
586 self.preparation_state == PreparationState::Preliminary,
587 "configuration not properly prepared"
588 );
589
590 Pcc::get_mut(&mut c)
592 .unwrap()
593 .insert(std::mem::take(&mut self.host_aliases));
594
595 macro_rules! prep {
596 ($server:ident) => {
597 if let Some(ref mut server) = self.$server {
598 server.prepare(c.clone()).await?;
599 }
600 };
601 }
602
603 for_all_servers!(prep);
604
605 drop(std::mem::replace(
607 &mut self.host_aliases,
608 Pcc::get_mut(&mut c)
609 .unwrap()
610 .remove::<HostAliases>()
611 .unwrap(),
612 ));
613
614 self.preparation_state = PreparationState::Complete;
615
616 Ok(())
617 }
618}
619
620impl<Extra: PrepareConfig<Pcc> + GetServerType> PrepareConfig<Pcc> for ServerConfig<Extra> {
621 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
622 if self.port == 0 {
623 self.port = Extra::ServerT::default_port();
624 }
625
626 self.self_check_code
627 .get_or_insert_with(crate::misc::crypto::random_alphanumeric);
628
629 if self.signing_key.is_none() {
630 self.signing_key = Some(
631 api::SigningKey::generate()
632 .map_err(|_| anyhow::anyhow!("failed to generate signing key"))?
633 .encode(),
634 );
635 }
636 self.enc_key.get_or_insert_with(|| {
637 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
638 });
639
640 if self.admin_key.is_none() {
641 let admin_key =
642 serde_ext::bytes_wrapper::B16::from_bytes(crate::misc::crypto::random_32_bytes());
643
644 log::info!("{} admin key: {admin_key}", Extra::ServerT::NAME);
645
646 self.admin_key = Some(admin_key);
647 }
648
649 if let &mut Some(&mut ref mut osc) = &mut self.object_store.as_mut() {
650 c.get::<HostAliases>()
651 .expect("host aliases were not passed along")
652 .dealias(&mut osc.url);
653 }
654
655 self.extra.prepare(c).await?;
656
657 Ok(())
658 }
659}
660
661fn ensure_decap_key(slot: &mut Option<kem::DecapKeyBytes>) -> anyhow::Result<()> {
663 if slot.is_none() {
664 *slot = Some(
665 kem::DecapKey::generate()
666 .and_then(|dk| dk.encode())
667 .map_err(|_| anyhow::anyhow!("generating kem decapsulation key"))?,
668 );
669 }
670 Ok(())
671}
672
673impl PrepareConfig<Pcc> for transcryptor::ExtraConfig {
674 async fn prepare(&mut self, _c: Pcc) -> anyhow::Result<()> {
675 self.master_enc_key_part
676 .get_or_insert_with(elgamal::PrivateKey::random);
677
678 self.pseud_factor_secret.get_or_insert_with(|| {
679 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
680 });
681
682 ensure_decap_key(&mut self.decap_key)?;
683
684 Ok(())
685 }
686}
687
688impl PrepareConfig<Pcc> for phc::ExtraConfig {
689 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
690 self.master_enc_key_part
691 .get_or_insert_with(elgamal::PrivateKey::random);
692
693 self.attr_id_secret.get_or_insert_with(|| {
694 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
695 });
696
697 self.user_object_hmac_secret.get_or_insert_with(|| {
698 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
699 });
700
701 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
702
703 ha.dealias(&mut self.transcryptor_url);
704 ha.dealias(&mut self.auths_url);
705 ha.dealias(&mut self.global_client_url);
706
707 for hub in self.hubs.iter_mut() {
708 hub.prepare(c.clone()).await?;
709 }
710
711 Ok(())
712 }
713}
714
715impl PrepareConfig<Pcc> for hub::BasicInfo<UrlPwa> {
716 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
717 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
718
719 ha.dealias(&mut self.url);
720
721 Ok(())
722 }
723}
724
725impl PrepareConfig<Pcc> for auths::ExtraConfig {
726 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
727 if let Some(ref mut yivi_cfg) = self.yivi {
728 yivi_cfg.prepare(c).await?;
729 }
730
731 self.filter_attribute_types();
732
733 self.attr_key_secret.get_or_insert_with(|| {
734 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
735 });
736
737 ensure_decap_key(&mut self.decap_key)?;
738
739 Ok(())
740 }
741}
742
743impl PrepareConfig<Pcc> for auths::YiviConfig {
744 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
745 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
746
747 ha.dealias(&mut self.requestor_url);
748
749 if self.server_key.is_none() {
750 let pk_url = self.requestor_url.as_ref().join("publickey")?.to_string();
751 log::debug!("yivi server key not set; retrieving from {pk_url}");
752
753 let client = awc::Client::default();
754 let payload: bytes::Bytes = crate::misc::task::retry(|| async {
755 match client.get(&pk_url).send().await {
756 Ok(mut res) => match res.body().await {
757 Ok(body) => Ok::<_, std::convert::Infallible>(Some(body)),
758 Err(err) => {
759 log::warn!(
760 "error reading yivi server response at {}, retrying: {err}",
761 self.requestor_url
762 );
763 Ok(None)
764 }
765 },
766 Err(err) => {
767 log::warn!(
768 "could not reach yivi server at {}, retrying: {err}",
769 self.requestor_url
770 );
771 Ok(None)
772 }
773 }
774 })
775 .await
776 .expect("retry does not fail")
777 .ok_or_else(|| {
778 log::error!("could not reach yivi server at {}", self.requestor_url);
779 anyhow::anyhow!(
780 "getting Yivi server's public key from {pk_url} failed after retries"
781 )
782 })?;
783
784 self.server_key = Some(yivi::VerifyingKey::RS256(
785 jwt::RS256Vk::from_public_key_pem(std::str::from_utf8(&payload)?)
786 .context("decoding public key at {pk_url}")?,
787 ));
788 }
789
790 Ok(())
791 }
792}
793
794pub trait GetServerConfig {
796 type Extra;
797
798 fn server_config(config: &Config) -> &ServerConfig<Self::Extra>;
799}
800
801macro_rules! implement_get_server_config {
802 ($server:ident) => {
803 impl GetServerConfig for crate::servers::$server::Details {
804 type Extra = crate::servers::config::$server::ExtraConfig;
805
806 fn server_config(config: &Config) -> &ServerConfig<Self::Extra> {
807 &config.$server.as_ref().unwrap()
808 }
809 }
810 };
811}
812
813for_all_servers!(implement_get_server_config);
814
815trait GetServerType {
817 type ServerT: crate::servers::Server;
818}
819
820macro_rules! implement_server_type {
821 ($server:ident) => {
822 impl GetServerType for $server::ExtraConfig {
823 type ServerT = crate::servers::$server::Server;
824 }
825 };
826}
827
828for_all_servers!(implement_server_type);