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")]
380 #[serde(default = "default_pp_nonce_validity")]
381 pub pp_nonce_validity: core::time::Duration,
382
383 #[serde(with = "time_ext::human_duration")]
386 #[serde(default = "default_card_pseud_validity")]
387 pub card_pseud_validity: core::time::Duration,
388
389 pub user_object_hmac_secret: Option<B64UU>,
393
394 #[serde(default)]
396 pub user_quota: api::phc::user::Quota,
397
398 #[serde(default, skip_serializing)]
400 pub card: serde::de::IgnoredAny,
401
402 #[serde(default)]
403 pub hub_cache: crate::servers::phc::HubCacheConfig,
404 }
405
406 fn default_auth_token_validity() -> core::time::Duration {
407 core::time::Duration::from_secs(60 * 60) }
410
411 fn default_pp_nonce_validity() -> core::time::Duration {
412 core::time::Duration::from_secs(30)
413 }
415
416 fn default_card_pseud_validity() -> core::time::Duration {
417 core::time::Duration::from_secs(30)
418 }
420}
421
422pub mod transcryptor {
423 use super::*;
424
425 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
426 #[serde(deny_unknown_fields)]
427 pub struct ExtraConfig {
428 pub master_enc_key_part: Option<elgamal::PrivateKey>,
432
433 pub pseud_factor_secret: Option<B64UU>,
439
440 pub decap_key: Option<kem::DecapKeyBytes>,
445 }
446}
447
448pub mod auths {
449 use super::*;
450
451 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
452 #[serde(deny_unknown_fields)]
453 pub struct ExtraConfig {
454 #[serde(default)]
455 pub attribute_types: Vec<attr::Type>,
456
457 pub yivi: Option<YiviConfig>,
459
460 #[serde(with = "time_ext::human_duration")]
463 #[serde(default = "default_auth_window")]
464 pub auth_window: core::time::Duration,
465
466 #[serde(default = "default_max_attr_types_per_req")]
472 pub max_attr_types_per_req: usize,
473
474 pub attr_key_secret: Option<B64UU>,
479
480 pub decap_key: Option<kem::DecapKeyBytes>,
485 }
486
487 fn default_auth_window() -> core::time::Duration {
488 core::time::Duration::from_secs(60 * 60) }
491
492 fn default_max_attr_types_per_req() -> usize {
493 4
494 }
495
496 impl ExtraConfig {
497 pub(super) fn filter_attribute_types(&mut self) {
502 let mut supported_sources: std::collections::HashSet<attr::Source> = Default::default();
503
504 if self.yivi.is_some() {
505 assert!(supported_sources.insert(attr::Source::Yivi));
506 }
507
508 for attr_type in self.attribute_types.iter_mut() {
509 attr_type.filter_sources(|s| supported_sources.contains(&s))
510 }
511 }
512 }
513
514 #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
515 #[serde(deny_unknown_fields)]
516 pub struct YiviConfig {
517 pub requestor_url: UrlPwa,
520
521 pub requestor_creds: yivi::Credentials<yivi::SigningKey>,
522
523 pub server_name: String,
525
526 pub server_key: Option<yivi::VerifyingKey>,
529
530 #[serde(default)]
533 pub chained_sessions: crate::servers::auths::yivi::ChainedSessionsConfig,
534
535 #[serde(default)]
537 pub card: crate::servers::auths::card::CardConfig,
538 }
539
540 impl YiviConfig {
541 pub fn server_creds(&self) -> yivi::Credentials<yivi::VerifyingKey> {
542 yivi::Credentials {
543 name: self.server_name.clone(),
544 key: self
545 .server_key
546 .clone()
547 .expect("bug: YiviConfig was not properly prepared"),
548 }
549 }
550 }
551}
552
553trait PrepareConfig<C> {
556 async fn prepare(&mut self, context: C) -> anyhow::Result<()>;
557}
558
559type Pcc = std::rc::Rc<actix_web::dev::Extensions>;
560
561impl PrepareConfig<Pcc> for Config {
562 async fn prepare(&mut self, mut c: Pcc) -> anyhow::Result<()> {
563 anyhow::ensure!(
564 self.preparation_state == PreparationState::Preliminary,
565 "configuration not properly prepared"
566 );
567
568 Pcc::get_mut(&mut c)
570 .unwrap()
571 .insert(std::mem::take(&mut self.host_aliases));
572
573 macro_rules! prep {
574 ($server:ident) => {
575 if let Some(ref mut server) = self.$server {
576 server.prepare(c.clone()).await?;
577 }
578 };
579 }
580
581 for_all_servers!(prep);
582
583 drop(std::mem::replace(
585 &mut self.host_aliases,
586 Pcc::get_mut(&mut c)
587 .unwrap()
588 .remove::<HostAliases>()
589 .unwrap(),
590 ));
591
592 self.preparation_state = PreparationState::Complete;
593
594 Ok(())
595 }
596}
597
598impl<Extra: PrepareConfig<Pcc> + GetServerType> PrepareConfig<Pcc> for ServerConfig<Extra> {
599 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
600 if self.port == 0 {
601 self.port = Extra::ServerT::default_port();
602 }
603
604 self.self_check_code
605 .get_or_insert_with(crate::misc::crypto::random_alphanumeric);
606
607 if self.signing_key.is_none() {
608 self.signing_key = Some(
609 api::SigningKey::generate()
610 .map_err(|_| anyhow::anyhow!("failed to generate signing key"))?
611 .encode(),
612 );
613 }
614 self.enc_key.get_or_insert_with(|| {
615 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
616 });
617
618 if self.admin_key.is_none() {
619 let admin_key =
620 serde_ext::bytes_wrapper::B16::from_bytes(crate::misc::crypto::random_32_bytes());
621
622 log::info!("{} admin key: {admin_key}", Extra::ServerT::NAME);
623
624 self.admin_key = Some(admin_key);
625 }
626
627 if let &mut Some(&mut ref mut osc) = &mut self.object_store.as_mut() {
628 c.get::<HostAliases>()
629 .expect("host aliases were not passed along")
630 .dealias(&mut osc.url);
631 }
632
633 self.extra.prepare(c).await?;
634
635 Ok(())
636 }
637}
638
639fn ensure_decap_key(slot: &mut Option<kem::DecapKeyBytes>) -> anyhow::Result<()> {
641 if slot.is_none() {
642 *slot = Some(
643 kem::DecapKey::generate()
644 .and_then(|dk| dk.encode())
645 .map_err(|_| anyhow::anyhow!("generating kem decapsulation key"))?,
646 );
647 }
648 Ok(())
649}
650
651impl PrepareConfig<Pcc> for transcryptor::ExtraConfig {
652 async fn prepare(&mut self, _c: Pcc) -> anyhow::Result<()> {
653 self.master_enc_key_part
654 .get_or_insert_with(elgamal::PrivateKey::random);
655
656 self.pseud_factor_secret.get_or_insert_with(|| {
657 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
658 });
659
660 ensure_decap_key(&mut self.decap_key)?;
661
662 Ok(())
663 }
664}
665
666impl PrepareConfig<Pcc> for phc::ExtraConfig {
667 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
668 self.master_enc_key_part
669 .get_or_insert_with(elgamal::PrivateKey::random);
670
671 self.attr_id_secret.get_or_insert_with(|| {
672 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
673 });
674
675 self.user_object_hmac_secret.get_or_insert_with(|| {
676 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
677 });
678
679 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
680
681 ha.dealias(&mut self.transcryptor_url);
682 ha.dealias(&mut self.auths_url);
683 ha.dealias(&mut self.global_client_url);
684
685 for hub in self.hubs.iter_mut() {
686 hub.prepare(c.clone()).await?;
687 }
688
689 Ok(())
690 }
691}
692
693impl PrepareConfig<Pcc> for hub::BasicInfo<UrlPwa> {
694 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
695 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
696
697 ha.dealias(&mut self.url);
698
699 Ok(())
700 }
701}
702
703impl PrepareConfig<Pcc> for auths::ExtraConfig {
704 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
705 if let Some(ref mut yivi_cfg) = self.yivi {
706 yivi_cfg.prepare(c).await?;
707 }
708
709 self.filter_attribute_types();
710
711 self.attr_key_secret.get_or_insert_with(|| {
712 serde_bytes::ByteBuf::from(crate::misc::crypto::random_32_bytes()).into()
713 });
714
715 ensure_decap_key(&mut self.decap_key)?;
716
717 Ok(())
718 }
719}
720
721impl PrepareConfig<Pcc> for auths::YiviConfig {
722 async fn prepare(&mut self, c: Pcc) -> anyhow::Result<()> {
723 let ha: &HostAliases = c.get::<HostAliases>().unwrap();
724
725 ha.dealias(&mut self.requestor_url);
726
727 if self.server_key.is_none() {
728 let pk_url = self.requestor_url.as_ref().join("publickey")?.to_string();
729 log::debug!("yivi server key not set; retrieving from {pk_url}");
730
731 let client = awc::Client::default();
732 let payload: bytes::Bytes = crate::misc::task::retry(|| async {
733 match client.get(&pk_url).send().await {
734 Ok(mut res) => match res.body().await {
735 Ok(body) => Ok::<_, std::convert::Infallible>(Some(body)),
736 Err(err) => {
737 log::warn!(
738 "error reading yivi server response at {}, retrying: {err}",
739 self.requestor_url
740 );
741 Ok(None)
742 }
743 },
744 Err(err) => {
745 log::warn!(
746 "could not reach yivi server at {}, retrying: {err}",
747 self.requestor_url
748 );
749 Ok(None)
750 }
751 }
752 })
753 .await
754 .expect("retry does not fail")
755 .ok_or_else(|| {
756 log::error!("could not reach yivi server at {}", self.requestor_url);
757 anyhow::anyhow!(
758 "getting Yivi server's public key from {pk_url} failed after retries"
759 )
760 })?;
761
762 self.server_key = Some(yivi::VerifyingKey::RS256(
763 jwt::RS256Vk::from_public_key_pem(std::str::from_utf8(&payload)?)
764 .context("decoding public key at {pk_url}")?,
765 ));
766 }
767
768 Ok(())
769 }
770}
771
772pub trait GetServerConfig {
774 type Extra;
775
776 fn server_config(config: &Config) -> &ServerConfig<Self::Extra>;
777}
778
779macro_rules! implement_get_server_config {
780 ($server:ident) => {
781 impl GetServerConfig for crate::servers::$server::Details {
782 type Extra = crate::servers::config::$server::ExtraConfig;
783
784 fn server_config(config: &Config) -> &ServerConfig<Self::Extra> {
785 &config.$server.as_ref().unwrap()
786 }
787 }
788 };
789}
790
791for_all_servers!(implement_get_server_config);
792
793trait GetServerType {
795 type ServerT: crate::servers::Server;
796}
797
798macro_rules! implement_server_type {
799 ($server:ident) => {
800 impl GetServerType for $server::ExtraConfig {
801 type ServerT = crate::servers::$server::Server;
802 }
803 };
804}
805
806for_all_servers!(implement_server_type);