mpc_relay_server/
config.rs1use mpc_protocol::{decode_keypair, hex, Keypair};
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5use tokio::fs;
6use url::Url;
7
8use crate::{Error, Result};
9
10const ENV_PEM: &str = "MPC_RELAY_KEYPAIR";
12
13#[derive(Default, Serialize, Deserialize)]
15#[serde(default)]
16pub struct ServerConfig {
17 pub key: PathBuf,
19
20 pub pattern: Option<String>,
22
23 pub session: SessionConfig,
25
26 pub tls: Option<TlsConfig>,
28
29 pub allow: Option<Vec<AccessKey>>,
32
33 pub deny: Option<Vec<AccessKey>>,
36
37 pub cors: CorsConfig,
39}
40
41impl ServerConfig {
42 pub fn is_allowed_access(&self, key: impl AsRef<[u8]>) -> bool {
44 if let Some(deny) = &self.deny {
47 if deny.iter().any(|k| k.public_key == key.as_ref()) {
48 return false;
49 }
50 }
51
52 if let Some(allow) = &self.allow {
53 if allow.iter().any(|k| k.public_key == key.as_ref()) {
54 return true;
55 }
56 false
57 } else {
58 true
59 }
60 }
61}
62
63#[derive(Default, Serialize, Deserialize)]
64pub struct AccessKey {
65 #[serde(with = "hex::serde")]
66 public_key: Vec<u8>,
67}
68
69#[derive(Debug, Default, Clone, Serialize, Deserialize)]
71pub struct TlsConfig {
72 pub cert: PathBuf,
74 pub key: PathBuf,
76}
77
78#[derive(Debug, Serialize, Deserialize)]
80#[serde(rename_all = "kebab-case")]
81pub struct SessionConfig {
82 pub timeout: u64,
89
90 pub interval: u64,
94
95 pub wait_interval: u64,
104
105 pub wait_timeout: u64,
110}
111
112impl Default for SessionConfig {
113 fn default() -> Self {
114 Self {
115 timeout: 300,
116 interval: 900,
117 wait_interval: 15,
118 wait_timeout: 300,
119 }
120 }
121}
122
123impl ServerConfig {
124 pub async fn load<P: AsRef<Path>>(
126 path: P,
127 ) -> Result<(Self, Keypair)> {
128 if !fs::try_exists(path.as_ref()).await? {
129 return Err(Error::NotFile(path.as_ref().to_path_buf()));
130 }
131
132 let contents = fs::read_to_string(path.as_ref()).await?;
133 let mut config: ServerConfig = toml::from_str(&contents)?;
134
135 if config.session.interval <= config.session.timeout {
136 return Err(Error::SessionTimeoutConfig);
137 }
138
139 if config.session.wait_timeout <= config.session.wait_interval
140 {
141 return Err(Error::SessionWaitConfig);
142 }
143
144 let dir = Self::directory(path.as_ref())?;
145
146 let contents = if let Ok(env_keypair) = std::env::var(ENV_PEM)
147 {
148 tracing::info!("use environmet keypair PEM");
149 env_keypair.to_owned()
150 } else {
151 if config.key == PathBuf::default() {
152 return Err(Error::KeyFileRequired);
153 }
154
155 if config.key.is_relative() {
156 config.key = dir.join(&config.key).canonicalize()?;
157 }
158
159 if !fs::try_exists(&config.key).await? {
160 return Err(Error::KeyNotFound(config.key.clone()));
161 }
162
163 fs::read_to_string(&config.key).await?
164 };
165
166 let keypair = decode_keypair(contents)?;
167
168 if let Some(tls) = config.tls.as_mut() {
169 if tls.cert.is_relative() {
170 tls.cert = dir.join(&tls.cert).canonicalize()?;
171 }
172 if tls.key.is_relative() {
173 tls.key = dir.join(&tls.key).canonicalize()?;
174 }
175 }
176
177 Ok((config, keypair))
178 }
179
180 fn directory(file: impl AsRef<Path>) -> Result<PathBuf> {
182 file.as_ref()
183 .parent()
184 .map(|p| p.to_path_buf())
185 .ok_or_else(|| Error::NoParentDir)
186 }
187}
188
189#[derive(Debug, Default, Serialize, Deserialize)]
191pub struct CorsConfig {
192 pub origins: Vec<Url>,
194}