trustchain_http/
server.rs

1//! Trustchain HTTP router type and functionality for spawning HTTP and HTTPS servers.
2
3use crate::attestor;
4use crate::config::http_config;
5use crate::middleware::validate_did;
6use crate::{
7    config::HTTPConfig, issuer, resolver, root, state::AppState, static_handlers, verifier,
8};
9use axum::routing::{post, IntoMakeService};
10use axum::{middleware, routing::get, Router};
11use axum_server::tls_rustls::RustlsConfig;
12use hyper::server::conn::AddrIncoming;
13use std::path::PathBuf;
14use std::sync::Arc;
15use tower::ServiceBuilder;
16
17/// A wrapped axum router combining Trustchain HTTP handlers for resolution (DIDs, chains, bundles),
18/// verification (VCs and VPs), issuance (VCs), root candidate retrieval and communication with an
19/// ION node.
20pub struct TrustchainRouter {
21    router: Router,
22}
23
24impl From<Arc<AppState>> for TrustchainRouter {
25    fn from(app_state: Arc<AppState>) -> Self {
26        Self::new(app_state)
27    }
28}
29
30impl From<HTTPConfig> for TrustchainRouter {
31    fn from(config: HTTPConfig) -> Self {
32        let app_state = Arc::new(AppState::new(config));
33        Self::new(app_state)
34    }
35}
36
37impl TrustchainRouter {
38    /// Constructs a router given a ServerConfig.
39    fn new(shared_state: Arc<AppState>) -> Self {
40        Self {
41            router: Router::new()
42                .route("/", get(static_handlers::index))
43                .route(
44                    "/issuer/:id",
45                    get(issuer::TrustchainIssuerHTTPHandler::get_issuer_qrcode),
46                )
47                .route(
48                    "/verifier",
49                    get(verifier::TrustchainVerifierHTTPHandler::get_verifier_qrcode),
50                )
51                .route(
52                    "/vc/issuer/:id",
53                    get(issuer::TrustchainIssuerHTTPHandler::get_issuer).post({
54                        let state = shared_state.clone();
55                        move |(id, vc_info)| {
56                            issuer::TrustchainIssuerHTTPHandler::post_issuer((id, vc_info), state)
57                        }
58                    }),
59                )
60                .route(
61                    "/vc/verifier/:id",
62                    get(verifier::TrustchainVerifierHTTPHandler::get_verifier).post({
63                        let state = shared_state.clone();
64                        move |verification_info| {
65                            verifier::TrustchainVerifierHTTPHandler::post_verifier(
66                                verification_info,
67                                state,
68                            )
69                        }
70                    }),
71                )
72                .route(
73                    "/did/:id",
74                    get(resolver::TrustchainHTTPHandler::get_did_resolution)
75                        .layer(ServiceBuilder::new().layer(middleware::from_fn(validate_did))),
76                )
77                // Duplicate `did` and `identifier` routes as the resolver expects a
78                // `SidetreeClient` that can resolve at route `<ENDPOINT>/identifiers/<DID>`:
79                // See [here](https://docs.rs/did-ion/0.1.0/src/did_ion/sidetree.rs.html#1392-1400).
80                .route(
81                    "/identifiers/:id",
82                    get(resolver::TrustchainHTTPHandler::get_did_resolution)
83                        .layer(ServiceBuilder::new().layer(middleware::from_fn(validate_did))),
84                )
85                .route(
86                    "/did/chain/:id",
87                    get(resolver::TrustchainHTTPHandler::get_chain_resolution)
88                        .layer(ServiceBuilder::new().layer(middleware::from_fn(validate_did))),
89                )
90                .route(
91                    "/did/bundle/:id",
92                    get(resolver::TrustchainHTTPHandler::get_verification_bundle)
93                        .layer(ServiceBuilder::new().layer(middleware::from_fn(validate_did))),
94                )
95                .route(
96                    "/root",
97                    get(root::TrustchainRootHTTPHandler::get_root_candidates),
98                )
99                .route(
100                    "/root/timestamp/:height",
101                    get(root::TrustchainRootHTTPHandler::get_block_timestamp),
102                )
103                .route(
104                    "/operations",
105                    post({
106                        let state = shared_state.clone();
107                        move |operation| crate::ion::post_operation(operation, state)
108                    }),
109                )
110                .route(
111                    "/did/attestor/identity/initiate",
112                    post(attestor::TrustchainAttestorHTTPHandler::post_identity_initiation),
113                )
114                .route(
115                    "/did/attestor/identity/respond/:key_id",
116                    post({
117                        let state = shared_state.clone();
118                        move |(key_id, response)| {
119                            attestor::TrustchainAttestorHTTPHandler::post_identity_response(
120                                (key_id, response),
121                                state,
122                            )
123                        }
124                    }),
125                )
126                .route(
127                    "/did/attestor/content/initiate/:key_id",
128                    // post(attestor::TrustchainAttestorHTTPHandler::post_content_initiation),
129                    post({
130                        let state = shared_state.clone();
131                        move |(key_id, ddid)| {
132                            attestor::TrustchainAttestorHTTPHandler::post_content_initiation(
133                                (key_id, ddid),
134                                state,
135                            )
136                        }
137                    }),
138                )
139                .route(
140                    "/did/attestor/content/respond/:key_id",
141                    post({
142                        let state = shared_state.clone();
143                        move |key_id| {
144                            attestor::TrustchainAttestorHTTPHandler::post_content_response(
145                                key_id, state,
146                            )
147                        }
148                    }),
149                )
150                .with_state(shared_state),
151        }
152    }
153
154    /// Moves wrapped app router and consumes.
155    pub fn into_router(self) -> Router {
156        self.router
157    }
158}
159
160/// Spawns a Trustchain server given `HTTPConfig` with http.
161pub fn http_server(config: HTTPConfig) -> axum::Server<AddrIncoming, IntoMakeService<Router>> {
162    let addr = config.to_socket_address();
163    let shared_state = Arc::new(AppState::new(config));
164    let app = TrustchainRouter::from(shared_state).into_router();
165    axum::Server::bind(&addr).serve(app.into_make_service())
166}
167
168/// Spawns a Trustchain server given `HTTPConfig` with https.
169pub async fn https_server(config: HTTPConfig) -> std::io::Result<()> {
170    let addr = config.to_socket_address();
171    let shared_state = Arc::new(AppState::new(config));
172    let app = TrustchainRouter::from(shared_state).into_router();
173    let tls_config = rustls_config(http_config().https_path.as_ref().unwrap()).await;
174    axum_server::bind_rustls(addr, tls_config)
175        .serve(app.into_make_service())
176        .await
177}
178
179/// Generates a `RustlsConfig` for https servers given a path with certificate and key. Based on
180/// axum [example](https://github.com/tokio-rs/axum/blob/d30375925dd22cc44aeaae2871f8ead1630fadf8/examples/tls-rustls/src/main.rs).
181async fn rustls_config(path: &str) -> RustlsConfig {
182    // Configure certificate and private key used by https
183    let path = shellexpand::tilde(path);
184    RustlsConfig::from_pem_file(
185        PathBuf::from(path.as_ref()).join("cert.pem"),
186        PathBuf::from(path.as_ref()).join("key.pem"),
187    )
188    .await
189    .expect("Failed to create Rustls config.")
190}