use mpc_relay_protocol::{decode_keypair, hex, snow::Keypair};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tokio::fs;
use crate::{Error, Result};
#[derive(Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
pub key: PathBuf,
pub session: SessionConfig,
pub tls: Option<TlsConfig>,
pub allow: Option<Vec<AccessKey>>,
pub deny: Option<Vec<AccessKey>>,
}
impl ServerConfig {
pub fn is_allowed_access(&self, key: impl AsRef<[u8]>) -> bool {
if let Some(deny) = &self.deny {
if deny.iter().any(|k| k.public_key == key.as_ref()) {
return false;
}
}
if let Some(allow) = &self.allow {
if allow.iter().any(|k| k.public_key == key.as_ref()) {
return true;
}
false
} else {
true
}
}
}
#[derive(Default, Serialize, Deserialize)]
pub struct AccessKey {
#[serde(with = "hex::serde")]
public_key: Vec<u8>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
pub cert: PathBuf,
pub key: PathBuf,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SessionConfig {
pub timeout: u64,
pub reap_interval: u64,
}
impl Default for SessionConfig {
fn default() -> Self {
Self {
timeout: 300,
reap_interval: 1800,
}
}
}
impl ServerConfig {
pub async fn load<P: AsRef<Path>>(
path: P,
) -> Result<(Self, Keypair)> {
if !fs::try_exists(path.as_ref()).await? {
return Err(Error::NotFile(path.as_ref().to_path_buf()));
}
let contents = fs::read_to_string(path.as_ref()).await?;
let mut config: ServerConfig = toml::from_str(&contents)?;
if config.key == PathBuf::default() {
return Err(Error::KeyFileRequired);
}
let dir = Self::directory(path.as_ref())?;
if config.key.is_relative() {
config.key = dir.join(&config.key).canonicalize()?;
}
if !fs::try_exists(&config.key).await? {
return Err(Error::KeyNotFound(config.key.clone()));
}
let contents = fs::read_to_string(&config.key).await?;
let keypair = decode_keypair(contents)?;
if let Some(tls) = config.tls.as_mut() {
if tls.cert.is_relative() {
tls.cert = dir.join(&tls.cert).canonicalize()?;
}
if tls.key.is_relative() {
tls.key = dir.join(&tls.key).canonicalize()?;
}
}
Ok((config, keypair))
}
fn directory(file: impl AsRef<Path>) -> Result<PathBuf> {
file.as_ref()
.parent()
.map(|p| p.to_path_buf())
.ok_or_else(|| Error::NoParentDir)
}
}