1use actix_web::web;
3use anyhow::{Context as _, Result};
4use futures_util::future::FutureExt as _;
5
6use core::convert::Infallible;
7use std::ops::{Deref, DerefMut};
8use std::rc::Rc;
9
10use crate::common::{elgamal, kem};
11
12use crate::client;
13
14use crate::api::OpenError;
15use crate::api::{
16 self, ApiResultExt as _, DiscoveryRunResp, EndpointDetails, NoPayload, ResultExt as _,
17};
18use crate::servers::{self, Config, Constellation, Handle};
19
20#[derive(
22 serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash, clap::ValueEnum,
23)]
24pub enum Name {
25 #[serde(rename = "phc")]
26 PubhubsCentral,
27
28 #[serde(rename = "transcryptor")]
29 Transcryptor,
30
31 #[serde(rename = "auths")]
32 AuthenticationServer,
33}
34
35impl std::fmt::Display for Name {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
37 write!(
38 f,
39 "{}",
40 match self {
41 Name::PubhubsCentral => "PubHubs Central",
42 Name::Transcryptor => "Transcryptor",
43 Name::AuthenticationServer => "Authentication Server",
44 }
45 )
46 }
47}
48
49pub trait Server: DerefMut<Target = Self::AppCreatorT> + Sized + 'static {
61 type AppT: App<Self>;
62
63 const NAME: Name;
64
65 fn default_port() -> u16 {
67 match Self::NAME {
68 Name::PubhubsCentral => 5050,
71 Name::Transcryptor => 7070,
72 Name::AuthenticationServer => 6060,
73 }
74 }
75
76 type AppCreatorT: AppCreator<Self>;
78
79 type ExtraConfig;
80
81 type ExtraRunningState: Clone + core::fmt::Debug;
83
84 type RunningStateSeed: Send + 'static;
88
89 type ExtraSharedState;
91
92 type ExtraServerState;
94
95 type ObjectStoreT: Sync;
97
98 fn new(config: &crate::servers::Config) -> Result<Self>;
99
100 fn config(&self) -> &crate::servers::Config;
101
102 fn server_config(&self) -> &servers::config::ServerConfig<Self::ExtraConfig> {
103 Self::server_config_from(self.config())
104 }
105
106 fn server_config_from(
107 config: &servers::Config,
108 ) -> &servers::config::ServerConfig<Self::ExtraConfig>;
109
110 fn create_running_state(
111 &self,
112 constellation: &Constellation,
113 seed: &Self::RunningStateSeed,
114 ) -> Result<Self::ExtraRunningState>;
115
116 fn create_extra_shared_state(config: &servers::Config) -> Result<Self::ExtraSharedState>;
117
118 fn create_extra_server_state(config: &servers::Config) -> Result<Self::ExtraServerState>;
119
120 fn extra(&self) -> &Self::ExtraServerState;
121
122 #[expect(async_fn_in_trait)]
141 async fn run_until_modifier(
142 self: Rc<Self>,
143 shutdown_receiver: tokio::sync::oneshot::Receiver<Infallible>,
144 app: Rc<Self::AppT>,
145 ) -> Result<Option<BoxModifier<Self>>>;
146
147 fn cors() -> actix_cors::Cors {
149 actix_cors::Cors::default()
150 .allow_any_origin()
151 .allowed_methods(["GET", "POST"])
152 .allowed_header(actix_web::http::header::CONTENT_TYPE)
153 .allowed_header(actix_web::http::header::AUTHORIZATION)
154 }
155}
156
157pub struct ServerImpl<D: Details> {
159 config: servers::Config,
160 app_creator: D::AppCreatorT,
161 extra: D::ExtraServerState,
162}
163
164impl<D: Details> Deref for ServerImpl<D> {
165 type Target = D::AppCreatorT;
166
167 #[inline]
168 fn deref(&self) -> &Self::Target {
169 &self.app_creator
170 }
171}
172
173impl<D: Details> DerefMut for ServerImpl<D> {
174 #[inline]
175 fn deref_mut(&mut self) -> &mut Self::Target {
176 &mut self.app_creator
177 }
178}
179
180pub trait Details: crate::servers::config::GetServerConfig + 'static + Sized {
182 const NAME: Name;
183 type AppCreatorT;
184 type AppT;
185 type ExtraRunningState: Clone + core::fmt::Debug;
186 type RunningStateSeed: Send + 'static;
187 type ExtraSharedState;
188 type ExtraServerState;
189 type ObjectStoreT;
190
191 fn create_running_state(
192 server: &ServerImpl<Self>,
193 constellation: &Constellation,
194 seed: &Self::RunningStateSeed,
195 ) -> Result<Self::ExtraRunningState>;
196
197 fn create_extra_shared_state(config: &servers::Config) -> Result<Self::ExtraSharedState>;
198
199 fn create_extra_server_state(config: &servers::Config) -> Result<Self::ExtraServerState>;
200}
201
202impl<D: Details> Server for ServerImpl<D>
203where
204 D::AppT: App<Self>,
205 D::AppCreatorT: AppCreator<Self>,
206 D::ObjectStoreT: Sync,
207{
208 const NAME: Name = D::NAME;
209
210 type AppCreatorT = D::AppCreatorT;
211 type AppT = D::AppT;
212
213 type ExtraConfig = D::Extra;
214 type ExtraRunningState = D::ExtraRunningState;
215 type RunningStateSeed = D::RunningStateSeed;
216 type ExtraSharedState = D::ExtraSharedState;
217 type ExtraServerState = D::ExtraServerState;
218
219 type ObjectStoreT = D::ObjectStoreT;
220
221 fn new(config: &servers::Config) -> Result<Self> {
222 Ok(Self {
223 app_creator: Self::AppCreatorT::new(config)?,
224 extra: D::create_extra_server_state(config)?,
225 config: config.clone(),
226 })
227 }
228
229 fn config(&self) -> &servers::Config {
230 &self.config
231 }
232
233 fn server_config_from(
234 config: &servers::Config,
235 ) -> &servers::config::ServerConfig<Self::ExtraConfig> {
236 D::server_config(config)
237 }
238
239 fn create_running_state(
240 &self,
241 constellation: &Constellation,
242 seed: &Self::RunningStateSeed,
243 ) -> Result<Self::ExtraRunningState> {
244 D::create_running_state(self, constellation, seed)
245 }
246
247 fn create_extra_shared_state(config: &servers::Config) -> Result<Self::ExtraSharedState> {
248 D::create_extra_shared_state(config)
249 }
250
251 fn create_extra_server_state(config: &servers::Config) -> Result<Self::ExtraServerState> {
252 D::create_extra_server_state(config)
253 }
254
255 fn extra(&self) -> &Self::ExtraServerState {
256 &self.extra
257 }
258
259 async fn run_until_modifier(
260 self: Rc<Self>,
261 shutdown_receiver: tokio::sync::oneshot::Receiver<Infallible>,
262 app: Rc<Self::AppT>,
263 ) -> Result<Option<crate::servers::server::BoxModifier<Self>>> {
264 tokio::select! {
265 res = shutdown_receiver => {
266 res.expect_err("got instance of Infallible");
267 #[expect(clippy::needless_return)] return Ok(None);
269 }
270
271 res = self.run_discovery_and_then_wait_forever(app) => {
272 #[expect(clippy::needless_return)] return Err(res.expect_err("got instance of Infallible"));
274 }
275 }
276 }
277}
278
279impl<D: Details> ServerImpl<D>
280where
281 D::AppT: App<Self>,
282 D::AppCreatorT: AppCreator<Self>,
283 D::ObjectStoreT: Sync,
284{
285 async fn run_discovery_and_then_wait_forever(&self, app: Rc<D::AppT>) -> Result<Infallible> {
286 self.run_discovery(app.clone()).await?;
287
288 D::AppT::global_task(app).await }
290
291 async fn run_discovery(&self, app: Rc<D::AppT>) -> Result<()> {
292 crate::misc::task::retry(|| async {
293 (match
294 AppBase::<Self>::handle_discovery_run(app.clone()).await.retryable()?
295 {
296 Some(DiscoveryRunResp::Restarting | DiscoveryRunResp::UpToDate) => Ok(Some(())),
297 None => Ok(None),
298 }) as Result<Option<()>>
299 })
300 .await?
301 .ok_or_else(|| anyhow::anyhow!("timeout waiting for discovery of {server_name}", server_name = D::NAME))
302 }
303}
304
305pub trait AppCreator<ServerT: Server>:
307 DerefMut<Target = AppCreatorBase<ServerT>> + Send + Clone + 'static
308{
309 type ContextT: Default + Send + Clone + 'static;
312
313 fn new(config: &servers::Config) -> Result<Self>;
315
316 fn into_app(
324 self,
325 handle: &Handle<ServerT>,
326 context: &Self::ContextT,
327 generation: usize,
328 ) -> ServerT::AppT;
329}
330
331pub trait Modifier<ServerT: Server>: Send + 'static {
339 fn modify(self: Box<Self>, server: &mut ServerT) -> bool;
341
342 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>;
343}
344
345impl<S: Server, F: FnOnce(&mut S) -> bool + Send + 'static, D: std::fmt::Display + Send + 'static>
346 Modifier<S> for (F, D)
347{
348 fn modify(self: Box<Self>, server: &mut S) -> bool {
349 self.0(server)
350 }
351
352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
353 self.1.fmt(f)
354 }
355}
356
357pub struct Exiter;
359
360impl<S: Server> Modifier<S> for Exiter {
361 fn modify(self: Box<Self>, _server: &mut S) -> bool {
362 false
363 }
364
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
366 f.write_str("exiter")
367 }
368}
369
370pub type BoxModifier<S> = Box<dyn Modifier<S>>;
372
373impl<S: Server> std::fmt::Display for BoxModifier<S> {
374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
375 Modifier::fmt(&**self, f)
376 }
377}
378
379pub(crate) trait Inspector<ServerT: Server>: Send + 'static {
381 fn inspect(self: Box<Self>, server: &ServerT);
383
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>;
385}
386
387impl<S: Server, F: FnOnce(&S) + Send + 'static, D: std::fmt::Display + Send + 'static> Inspector<S>
388 for (F, D)
389{
390 fn inspect(self: Box<Self>, server: &S) {
391 self.0(server)
392 }
393
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
395 self.1.fmt(f)
396 }
397}
398
399pub type BoxInspector<S> = Box<dyn Inspector<S>>;
401
402impl<S: Server> std::fmt::Display for BoxInspector<S> {
403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
404 Inspector::fmt(&**self, f)
405 }
406}
407
408pub(crate) enum Command<S: Server> {
410 Modify(BoxModifier<S>),
416
417 Inspect(BoxInspector<S>),
419
420 Exit,
422}
423impl<S: Server> std::fmt::Display for Command<S> {
424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
425 match self {
426 Command::Inspect(inspector) => write!(f, "inspector {inspector}"),
427 Command::Modify(modifier) => write!(f, "modifier {modifier}"),
428 Command::Exit => write!(f, "exit"),
429 }
430 }
431}
432
433pub enum DiscoverVerdict<RunningStateSeed> {
436 Alright,
438
439 ConstellationOutdated {
443 new_constellation: Box<Constellation>,
444 seed: RunningStateSeed,
445 },
446
447 RunningStateOutdated { seed: RunningStateSeed },
452
453 BinaryOutdated,
455}
456
457#[allow(async_fn_in_trait)]
461pub trait App<S: Server>: Deref<Target = AppBase<S>> + 'static {
462 fn configure_actix_app(self: &Rc<Self>, sc: &mut web::ServiceConfig);
465
466 fn check_constellation(&self, constellation: &Constellation) -> bool;
468
469 async fn discover(
479 self: &Rc<Self>,
480 phc_inf: api::DiscoveryInfoResp,
481 ) -> api::Result<DiscoverVerdict<S::RunningStateSeed>>;
482
483 async fn discover_as_non_phc(
486 self: &Rc<Self>,
487 phc_inf: api::DiscoveryInfoResp,
488 ) -> api::Result<DiscoverVerdict<()>> {
489 log::debug!("{server_name}: running discovery", server_name = S::NAME);
490
491 if S::NAME == Name::PubhubsCentral {
492 log::error!(
493 "{} should implement discovery itself!",
494 Name::PubhubsCentral
495 );
496 return Err(api::ErrorCode::InternalError);
497 }
498
499 if let Some(ref phc_version) = phc_inf.version
500 && let Some(my_version) = &self.version
501 {
502 let phc_version = crate::servers::version::to_semver(phc_version).map_err(|err| {
503 log::error!(
504 "could not parse semantic version returned by PHC: {phc_version}: {err}"
505 );
506 api::ErrorCode::InternalError
507 })?;
508
509 let my_version = crate::servers::version::to_semver(my_version).map_err(|err| {
510 log::error!("could not parse my semantic version {my_version}: {err}");
511 api::ErrorCode::InternalError
512 })?;
513
514 if my_version < phc_version {
515 log::warn!(
516 "{server_name}: {phc}'s version ({phc_version}) > my version ({my_version})",
517 server_name = S::NAME,
518 phc = Name::PubhubsCentral
519 );
520 return Ok(DiscoverVerdict::BinaryOutdated);
521 }
522
523 if my_version > phc_version {
524 log::warn!(
525 "{server_name}: {phc}'s version {phc_version} is out-of-date - requesting rediscovery",
526 server_name = S::NAME,
527 phc = Name::PubhubsCentral
528 );
529
530 let _drr = self
531 .client
532 .query::<api::DiscoveryRun>(&phc_inf.phc_url, NoPayload)
533 .await
534 .into_server_result()?;
535 return Err(api::ErrorCode::PleaseRetry);
536 }
537 } else {
538 log::warn!(
539 "not checking my version ({my_version}) against phc's version ({phc_version})",
540 my_version = crate::servers::version::VERSION,
541 phc_version = phc_inf.version.unwrap_or_else(|| "n/a".to_string())
542 );
543 }
544
545 assert!(
546 phc_inf.constellation_or_id.is_some(),
547 "this `discover` method should only be run when phc_inf.constellation is some"
548 );
549
550 let Some(phc_inf_constellation) = phc_inf.constellation_or_id.unwrap().into_constellation()
551 else {
552 log::warn!(
553 "{server_name}: {phc} returned only its contellation id",
554 phc = Name::PubhubsCentral,
555 server_name = S::NAME
556 );
557 return Err(api::ErrorCode::InternalError);
558 };
559
560 if !self.check_constellation(&phc_inf_constellation) {
561 log::warn!(
562 "{server_name}: {phc}'s constellation seems to be out-of-date - requesting rediscovery",
563 server_name = S::NAME,
564 phc = Name::PubhubsCentral
565 );
566
567 let _drr = self
569 .client
570 .query::<api::DiscoveryRun>(&phc_inf.phc_url, NoPayload)
571 .await
572 .into_server_result()?;
573
574 return Err(api::ErrorCode::PleaseRetry);
578 }
579
580 log::trace!(
581 "{server_name}: {phc}'s constellation looks alright! ",
582 server_name = S::NAME,
583 phc = Name::PubhubsCentral
584 );
585
586 if let Some(rs) = self.running_state.as_ref()
587 && phc_inf_constellation.id == rs.constellation.id
588 {
589 log::info!(
590 "{server_name}: my constellation is up-to-date!",
591 server_name = S::NAME,
592 );
593
594 return Ok(DiscoverVerdict::Alright);
595 }
596
597 log::info!(
598 "{}: my constellation is {}",
599 S::NAME,
600 if self.running_state.is_some() {
601 "out of date"
602 } else {
603 "not yet set"
604 }
605 );
606
607 let url = phc_inf_constellation.url(S::NAME);
609
610 let di = self
612 .client
613 .query::<api::DiscoveryInfo>(url, NoPayload)
614 .await
615 .into_server_result()?;
616
617 let _di_again = client::discovery::DiscoveryInfoCheck {
618 name: S::NAME,
619 phc_url: &self.phc_url,
620 self_check_code: Some(&self.self_check_code),
621 constellation: None,
622 }
625 .check(di, url)?;
626
627 Ok(DiscoverVerdict::ConstellationOutdated {
628 new_constellation: Box::new(phc_inf_constellation),
629 seed: (),
630 })
631 }
632
633 fn master_enc_key_part(&self) -> Option<&elgamal::PrivateKey> {
635 if matches!(S::NAME, Name::PubhubsCentral | Name::Transcryptor) {
636 panic!("this default impl should have been overriden for PHC and T")
637 }
638 None
639 }
640
641 fn encap_key(&self) -> Option<&kem::EncapKeyBytes> {
643 None
644 }
645
646 fn master_enc_key_part_sealing_key(&self) -> Option<&api::SealingKey> {
650 panic!(
651 "master_enc_key_part_sealing_key is only invoked on (and overridden by) the transcryptor"
652 )
653 }
654
655 async fn local_task(_weak: std::rc::Weak<Self>) {}
657
658 async fn global_task(_app: std::rc::Rc<Self>) -> Result<Infallible> {
660 Ok(std::future::pending::<Infallible>().await)
661 }
662}
663
664pub struct AppCreatorBase<S: Server> {
666 pub running_state: Option<RunningState<S::ExtraRunningState>>,
667 pub phc_url: url::Url,
668 pub self_check_code: String,
669 pub enc_key: Box<[u8]>,
670 pub admin_key: crate::misc::jwt::HS256,
671 pub shared: SharedState<S>,
672 pub version: Option<String>,
673}
674
675impl<S: Server> Clone for AppCreatorBase<S> {
677 fn clone(&self) -> Self {
678 Self {
679 running_state: self.running_state.clone(),
680 phc_url: self.phc_url.clone(),
681 self_check_code: self.self_check_code.clone(),
682 enc_key: self.enc_key.clone(),
683 admin_key: self.admin_key.clone(),
684 shared: self.shared.clone(),
685 version: self.version.clone(),
686 }
687 }
688}
689
690impl<S: Server> AppCreatorBase<S>
691where
692 S::ObjectStoreT: for<'a> TryFrom<&'a Option<servers::config::ObjectStoreConfig>, Error = anyhow::Error>
693 + Sync,
694{
695 pub fn new(config: &crate::servers::Config) -> Result<Self> {
696 assert_eq!(
697 config.preparation_state,
698 crate::servers::config::PreparationState::Complete
699 );
700
701 let server_config = S::server_config_from(config);
702
703 let signing_key = server_config
708 .signing_key
709 .as_ref()
710 .expect("signing_key was not set nor generated")
711 .decode()
712 .map_err(|_| anyhow::anyhow!("invalid signing_key in config"))?;
713 let verifying_key_bytes = signing_key.verifying_key().encode();
716
717 let admin_key = crate::misc::jwt::HS256(
718 server_config
719 .admin_key
720 .as_ref()
721 .expect("admin_key was not set nor generated")
722 .clone()
723 .into_inner()
724 .into_vec(),
725 );
726
727 Ok(Self {
728 running_state: None,
729 self_check_code: server_config
730 .self_check_code
731 .clone()
732 .expect("self_check_code was not set nor generated"),
733 enc_key: <serde_bytes::ByteBuf as Clone>::clone(
734 server_config
735 .enc_key
736 .as_ref()
737 .expect("enc_key was not set nor generated"),
738 )
739 .into_vec()
740 .into_boxed_slice(),
741 phc_url: config.phc_url.as_ref().clone(),
742 admin_key,
743 shared: SharedState::new(SharedStateInner {
744 object_store: TryFrom::try_from(&server_config.object_store)
745 .with_context(|| format!("Creating object store for {}", S::NAME))?,
746 signing_key,
747 verifying_key_bytes,
748 extra: S::create_extra_shared_state(config)?,
749 }),
750 version: server_config.version.clone(),
751 })
752 }
753}
754
755pub struct AppBase<S: Server> {
759 pub running_state: Option<RunningState<S::ExtraRunningState>>,
760 pub handle: Handle<S>,
761 pub self_check_code: String,
762 pub phc_url: url::Url,
763 pub admin_key: crate::misc::jwt::HS256,
764 pub shared: SharedState<S>,
765 pub client: client::Client,
766 pub version: Option<String>,
767 pub thread_id: std::thread::ThreadId,
768 pub generation: usize,
769}
770
771impl<S: Server> Drop for AppBase<S> {
772 fn drop(&mut self) {
773 log::trace!(
774 "{}: app for generation {} that started on {:?} dropped",
775 S::NAME,
776 self.generation,
777 self.thread_id
778 );
779 }
780}
781
782impl<S: Server> AppBase<S> {
783 pub fn new(creator_base: AppCreatorBase<S>, handle: &Handle<S>, generation: usize) -> Self {
784 let thread_id = std::thread::current().id();
785
786 log::trace!(
787 "{}: app for generation {} started on {:?}",
788 S::NAME,
789 generation,
790 thread_id
791 );
792
793 Self {
794 running_state: creator_base.running_state,
795 handle: handle.clone(),
796 phc_url: creator_base.phc_url,
797 self_check_code: creator_base.self_check_code,
798 admin_key: creator_base.admin_key,
799 shared: creator_base.shared,
800 client: client::Client::builder()
801 .agent(client::Agent::Server(S::NAME))
802 .finish(),
803 version: creator_base.version,
804 thread_id,
805 generation,
806 }
807 }
808
809 pub fn running_state_or_please_retry(
812 &self,
813 ) -> Result<&RunningState<S::ExtraRunningState>, api::ErrorCode> {
814 self.running_state
815 .as_ref()
816 .ok_or(api::ErrorCode::PleaseRetry)
817 }
818
819 pub fn running_state_or_internal_error(
822 &self,
823 ) -> Result<&RunningState<S::ExtraRunningState>, api::ErrorCode> {
824 self.running_state.as_ref().ok_or_else(|| {
825 log::error!(
826 "{}: expected running state to be available, but it was not",
827 S::NAME
828 );
829 api::ErrorCode::InternalError
830 })
831 }
832
833 pub fn configure_actix_app(app: &Rc<S::AppT>, sc: &mut web::ServiceConfig) {
835 api::DiscoveryRun::add_to(app, sc, Self::handle_discovery_run);
836 api::DiscoveryInfo::caching_add_to(app, sc, Self::cached_handle_discovery_info);
837
838 api::admin::UpdateConfigEP::add_to(app, sc, Self::handle_admin_post_config);
839 api::admin::InfoEP::add_to(app, sc, Self::handle_admin_info);
840 }
841
842 pub async fn handle_hub_ping(
845 app: Rc<S::AppT>,
846 signed_req: web::Json<api::phc::hub::TicketSigned<api::server::PingReq>>,
847 ) -> api::Result<api::server::PingResp> {
848 let running_state = app.running_state_or_please_retry()?;
849
850 let ts_req = signed_req.into_inner();
851
852 let (req, hub_handle) = match ts_req.open(&running_state.phc_verifying_key) {
853 Ok(opened) => opened,
854 Err(toe) => return toe.default_verdict(api::server::PingResp::RetryWithNewTicket),
855 };
856
857 Ok(api::server::PingResp::Success {
858 hub_handle,
859 nonce: req.nonce,
860 served_by: S::NAME,
861 })
862 }
863
864 async fn handle_admin_post_config(
866 app: Rc<S::AppT>,
867 signed_req: web::Json<api::Signed<api::admin::UpdateConfigReq>>,
868 ) -> api::Result<api::admin::UpdateConfigResp> {
869 let signed_req = signed_req.into_inner();
870
871 let req = match signed_req.open(&app.admin_key, None) {
872 Ok(req) => req,
873 Err(OpenError::OtherConstellation(..)) | Err(OpenError::InternalError) => {
874 return Err(api::ErrorCode::InternalError);
875 }
876 Err(OpenError::OtherwiseInvalid) => return Err(api::ErrorCode::BadRequest),
877 Err(OpenError::Expired) => return Ok(api::admin::UpdateConfigResp::ResignRequest),
878 Err(OpenError::InvalidSignature) => {
879 return Ok(api::admin::UpdateConfigResp::InvalidAdminKey);
880 }
881 };
882
883 let config = app
888 .handle
889 .inspect(
890 "admin's retrieval of current configuration",
891 |server: &S| -> Config { server.config().clone() },
892 )
893 .await
894 .into_ec(|_| {
895 log::warn!("{}: failed to retrieve configuration from server", S::NAME,);
896 api::ErrorCode::PleaseRetry })?;
898
899 let mut new_config: Config = config
900 .json_updated(&req.pointer, req.new_value.clone())
901 .into_ec(|err| {
902 log::warn!(
903 "{}: failed to modify configuration at {} to {}: {err:#}",
904 S::NAME,
905 req.pointer,
906 req.new_value
907 );
908 api::ErrorCode::BadRequest
909 })?;
910
911 drop(config);
912
913 new_config.preliminary_prep().into_ec(|err| {
915 log::warn!(
916 "{}: failed to reprepare (preliminary step) modified configuration: {err}",
917 S::NAME
918 );
919 api::ErrorCode::BadRequest
920 })?;
921 new_config.prepare().await.into_ec(|err| {
922 log::warn!(
923 "{}: failed to reprepare modified configuration: {err}",
924 S::NAME
925 );
926 api::ErrorCode::BadRequest
927 })?;
928
929 app
931 .handle
932 .modify(
933 "admin update of current in-memory configuration",
934 move |server: &mut S| {
935
936 let new_server_maybe = S::new(&new_config);
937
938 if let Err(err) = new_server_maybe {
939 log::error!("Could not create new {} with changed configuration: {}. Restarting old server.", S::NAME, err);
940 return true; }
942
943 *server = new_server_maybe.unwrap();
944
945 true }
947 )
948 .await
949 .into_ec(|_| {
950 log::warn!("{}: failed to enqueue modification", S::NAME);
951 api::ErrorCode::PleaseRetry
952 })?;
953
954 Ok(api::admin::UpdateConfigResp::Success)
955 }
956
957 async fn handle_admin_info(
959 app: Rc<S::AppT>,
960 signed_req: web::Json<api::Signed<api::admin::InfoReq>>,
961 ) -> api::Result<api::admin::InfoResp> {
962 let signed_req = signed_req.into_inner();
963
964 let _req = match signed_req.open(&app.admin_key, None) {
965 Ok(req) => req,
966 Err(OpenError::OtherConstellation(..)) | Err(OpenError::InternalError) => {
967 return Err(api::ErrorCode::InternalError);
968 }
969 Err(OpenError::OtherwiseInvalid) => return Err(api::ErrorCode::BadRequest),
970 Err(OpenError::Expired) => return Ok(api::admin::InfoResp::ResignRequest),
971 Err(OpenError::InvalidSignature) => return Ok(api::admin::InfoResp::InvalidAdminKey),
972 };
973
974 let config = app
975 .handle
976 .inspect(
977 "admin's retrieval of current configuration",
978 |server: &S| -> Config { server.config().clone() },
979 )
980 .await
981 .into_ec(|_| {
982 log::warn!("{}: failed to retrieve configuration from server", S::NAME,);
983 api::ErrorCode::PleaseRetry })?;
985
986 Ok(api::admin::InfoResp::Success {
987 config: Box::new(config),
988 })
989 }
990
991 async fn handle_discovery_run(app: Rc<S::AppT>) -> api::Result<api::DiscoveryRunResp> {
994 app.handle.request_discovery(app.clone()).await
995 }
996
997 pub(super) async fn discover_phc(app: Rc<S::AppT>) -> api::Result<api::DiscoveryInfoResp> {
998 let pdi = app
999 .client
1000 .query::<api::DiscoveryInfo>(&app.phc_url, NoPayload)
1001 .await
1002 .into_server_result()?;
1003
1004 client::discovery::DiscoveryInfoCheck {
1005 phc_url: &app.phc_url,
1006 name: Name::PubhubsCentral,
1007 self_check_code: if S::NAME == Name::PubhubsCentral {
1008 Some(&app.self_check_code)
1009 } else {
1010 None
1011 },
1012 constellation: None,
1013 }
1017 .check(pdi, &app.phc_url)
1018 }
1019
1020 fn cached_handle_discovery_info(app: &S::AppT) -> api::Result<api::DiscoveryInfoResp> {
1021 use super::constellation::ConstellationOrId;
1022
1023 let constellation_or_id = app.running_state.as_ref().map(|rs| match S::NAME {
1025 Name::PubhubsCentral => ConstellationOrId::Constellation(
1026 AsRef::<Constellation>::as_ref(&rs.constellation)
1027 .clone()
1028 .into(),
1029 ),
1030 _ => ConstellationOrId::Id {
1031 id: rs.constellation.id,
1032 },
1033 });
1034
1035 let master_enc_key_part_hash = if matches!(S::NAME, Name::Transcryptor)
1040 && let Some(sk) = app.master_enc_key_part()
1041 {
1042 Some(crate::phcrypto::master_enc_key_part_hash(sk.public_key()))
1043 } else {
1044 None
1045 };
1046 let master_enc_key_part_sealed = if matches!(S::NAME, Name::Transcryptor)
1047 && let Some(sk) = app.master_enc_key_part()
1048 && let Some(key) = app.master_enc_key_part_sealing_key()
1049 {
1050 Some(api::Sealed::new(
1051 &api::MasterEncKeyPart(sk.public_key().clone()),
1052 key,
1053 )?)
1054 } else {
1055 None
1056 };
1057
1058 Ok(api::DiscoveryInfoResp {
1059 name: S::NAME,
1060 version: app.version.clone(),
1061 self_check_code: app.self_check_code.clone(),
1062 phc_url: app.phc_url.clone(),
1063 verifying_key: app.shared.verifying_key_bytes.clone(),
1064 master_enc_key_part_hash,
1065 master_enc_key_part_sealed,
1066 encap_key: app.encap_key().cloned(),
1067 constellation_or_id,
1068 })
1069 }
1070}
1071
1072pub struct AppMethod<App, F, EP: ?Sized> {
1075 app: Rc<App>,
1076 f: F,
1077 phantom: std::marker::PhantomData<EP>,
1078}
1079
1080impl<App, F: Clone, EP> Clone for AppMethod<App, F, EP> {
1083 fn clone(&self) -> Self {
1084 Self {
1085 app: self.app.clone(),
1086 f: self.f.clone(),
1087 phantom: std::marker::PhantomData,
1088 }
1089 }
1090}
1091
1092impl<App, F, EP: ?Sized> AppMethod<App, F, EP> {
1093 pub fn new(app: &Rc<App>, f: F) -> Self {
1095 AppMethod {
1096 app: app.clone(),
1097 f,
1098 phantom: std::marker::PhantomData,
1099 }
1100 }
1101}
1102
1103macro_rules! factory_tuple ({ $($param:ident)* } => {
1107 impl<Func, Fut, App, EP, $($param,)*> actix_web::Handler<($($param,)*)> for AppMethod<App, Func, EP>
1108 where
1109 Func: Fn(Rc<App>, $($param),*) -> Fut + Clone + 'static,
1110 Fut: core::future::Future,
1111 App: 'static,
1112 EP : EndpointDetails + 'static,
1113 Fut::Output : Into<EP::ResponseType>,
1114 {
1115 type Output = api::Responder<EP>;
1116 type Future = futures::future::Map<Fut, fn(Fut::Output)->api::Responder<EP>>;
1117
1118 #[inline]
1119 #[allow(non_snake_case)]
1122 fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future {
1123 (self.f)(self.app.clone(), $($param,)*).map(response_type_to_responder)
1124 }
1125 }
1126});
1127
1128fn response_type_to_responder<EP: EndpointDetails, T: Into<EP::ResponseType>>(
1130 output: T,
1131) -> api::Responder<EP> {
1132 api::Responder(output.into())
1133}
1134
1135factory_tuple! {}
1136factory_tuple! { A }
1137factory_tuple! { A B }
1138factory_tuple! { A B C }
1139factory_tuple! { A B C D }
1140factory_tuple! { A B C D E }
1141factory_tuple! { A B C D E F }
1142factory_tuple! { A B C D E F G }
1143factory_tuple! { A B C D E F G H }
1144factory_tuple! { A B C D E F G H I }
1145factory_tuple! { A B C D E F G H I J }
1146factory_tuple! { A B C D E F G H I J K }
1147factory_tuple! { A B C D E F G H I J K L }
1148factory_tuple! { A B C D E F G H I J K L M }
1149factory_tuple! { A B C D E F G H I J K L M N }
1150factory_tuple! { A B C D E F G H I J K L M N O }
1151factory_tuple! { A B C D E F G H I J K L M N O P }
1152
1153#[derive(Clone, Debug)]
1155pub struct RunningState<Extra: Clone + core::fmt::Debug> {
1156 pub constellation: Box<Constellation>,
1157
1158 pub phc_verifying_key: api::VerifyingKey,
1161
1162 extra: Extra,
1164}
1165
1166impl<Extra: Clone + core::fmt::Debug> RunningState<Extra> {
1167 pub(crate) fn new(constellation: Constellation, extra: Extra) -> Result<Self> {
1168 let phc_verifying_key = constellation
1169 .phc_verifying_key
1170 .decode()
1171 .map_err(|_| anyhow::anyhow!("constellation's phc_verifying_key does not decode"))?;
1172
1173 Ok(RunningState {
1174 constellation: Box::new(constellation),
1175 phc_verifying_key,
1176 extra,
1177 })
1178 }
1179}
1180
1181impl<Extra: Clone + core::fmt::Debug> Deref for RunningState<Extra> {
1182 type Target = Extra;
1183
1184 #[inline]
1185 fn deref(&self) -> &Extra {
1186 &self.extra
1187 }
1188}
1189
1190pub struct SharedState<S: Server> {
1192 inner: std::sync::Arc<SharedStateInner<S>>,
1193}
1194
1195impl<S: Server> Clone for SharedState<S> {
1196 fn clone(&self) -> Self {
1197 Self {
1198 inner: self.inner.clone(),
1199 }
1200 }
1201}
1202
1203impl<S: Server> std::ops::Deref for SharedState<S> {
1204 type Target = SharedStateInner<S>;
1205
1206 #[inline]
1207 fn deref(&self) -> &Self::Target {
1208 &self.inner
1209 }
1210}
1211
1212impl<S: Server> SharedState<S> {
1213 fn new(inner: SharedStateInner<S>) -> Self {
1214 Self {
1215 inner: std::sync::Arc::new(inner),
1216 }
1217 }
1218}
1219
1220pub struct SharedStateInner<S: Server> {
1221 pub object_store: S::ObjectStoreT,
1222
1223 pub signing_key: api::SigningKey,
1226
1227 pub verifying_key_bytes: api::VerifyingKeyBytes,
1230
1231 pub extra: S::ExtraSharedState,
1232}
1233
1234impl<S: Server> std::ops::Deref for SharedStateInner<S> {
1235 type Target = S::ExtraSharedState;
1236
1237 #[inline]
1238 fn deref(&self) -> &Self::Target {
1239 &self.extra
1240 }
1241}