Test config round trip

This commit is contained in:
Antoine Gersant 2024-10-08 23:03:15 -07:00
parent 0058221e88
commit fb18cb3c4f
6 changed files with 80 additions and 35 deletions
src
test-data

View file

@ -195,15 +195,14 @@ impl App {
async fn get_or_create_auth_secret(path: &Path) -> Result<auth::Secret, Error> {
match tokio::fs::read(&path).await {
Ok(s) => Ok(auth::Secret {
key: s
.try_into()
Ok(s) => Ok(auth::Secret(
s.try_into()
.map_err(|_| Error::AuthenticationSecretInvalid)?,
}),
)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
let mut secret = auth::Secret::default();
OsRng.fill_bytes(&mut secret.key);
tokio::fs::write(&path, &secret.key)
OsRng.fill_bytes(secret.as_mut());
tokio::fs::write(&path, &secret)
.await
.map_err(|_| Error::AuthenticationSecretInvalid)?;
Ok(secret)

View file

@ -9,8 +9,18 @@ use serde::{Deserialize, Serialize};
use crate::app::Error;
#[derive(Clone, Default)]
pub struct Secret {
pub key: [u8; 32],
pub struct Secret(pub [u8; 32]);
impl AsRef<[u8]> for Secret {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Secret {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
#[derive(Debug)]
@ -55,7 +65,7 @@ pub fn generate_auth_token(
serde_json::to_string(&authorization).or(Err(Error::AuthorizationTokenEncoding))?;
branca::encode(
serialized_authorization.as_bytes(),
&auth_secret.key,
auth_secret.as_ref(),
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
@ -75,7 +85,7 @@ pub fn decode_auth_token(
Scope::PolarisAuth => 0, // permanent
};
let authorization =
branca::decode(data, &auth_secret.key, ttl).map_err(|_| Error::InvalidAuthToken)?;
branca::decode(data, auth_secret.as_ref(), ttl).map_err(|_| Error::InvalidAuthToken)?;
let authorization: Authorization =
serde_json::from_slice(&authorization[..]).map_err(|_| Error::InvalidAuthToken)?;
if authorization.scope != scope {

View file

@ -24,7 +24,7 @@ pub struct Config {
pub reindex_every_n_seconds: Option<u64>,
pub album_art_pattern: Option<Regex>,
pub ddns_url: Option<http::Uri>,
pub mount_dirs: Vec<MountDir>,
pub mount_dirs: HashMap<String, MountDir>,
pub users: HashMap<String, User>,
}
@ -34,16 +34,15 @@ impl TryFrom<storage::Config> for Config {
fn try_from(c: storage::Config) -> Result<Self, Self::Error> {
let mut users: HashMap<String, User> = HashMap::new();
for user in c.users {
if let Ok(user) = <storage::User as TryInto<User>>::try_into(user) {
users.insert(user.name.clone(), user);
}
let user = <storage::User as TryInto<User>>::try_into(user)?;
users.insert(user.name.clone(), user);
}
let mount_dirs = c
.mount_dirs
.into_iter()
.filter_map(|m| m.try_into().ok())
.collect();
let mut mount_dirs: HashMap<String, MountDir> = HashMap::new();
for mount_dir in c.mount_dirs {
let mount_dir = <storage::MountDir as TryInto<MountDir>>::try_into(mount_dir)?;
mount_dirs.insert(mount_dir.name.clone(), mount_dir);
}
let ddns_url = match c.ddns_url.map(http::Uri::try_from) {
Some(Ok(u)) => Some(u),
@ -58,7 +57,7 @@ impl TryFrom<storage::Config> for Config {
};
Ok(Config {
reindex_every_n_seconds: c.reindex_every_n_seconds, // TODO validate and warn
reindex_every_n_seconds: c.reindex_every_n_seconds,
album_art_pattern,
ddns_url,
mount_dirs,
@ -72,7 +71,7 @@ impl From<Config> for storage::Config {
Self {
reindex_every_n_seconds: c.reindex_every_n_seconds,
album_art_pattern: c.album_art_pattern.map(|p| p.as_str().to_owned()),
mount_dirs: c.mount_dirs.into_iter().map(|d| d.into()).collect(),
mount_dirs: c.mount_dirs.into_iter().map(|(_, d)| d.into()).collect(),
ddns_url: c.ddns_url.map(|u| u.to_string()),
users: c.users.into_iter().map(|(_, u)| u.into()).collect(),
}
@ -208,7 +207,8 @@ impl Manager {
}
pub async fn get_mounts(&self) -> Vec<MountDir> {
self.config.read().await.mount_dirs.clone()
let config = self.config.read().await;
config.mount_dirs.values().cloned().collect()
}
pub async fn resolve_virtual_path<P: AsRef<Path>>(
@ -219,8 +219,42 @@ impl Manager {
config.resolve_virtual_path(virtual_path)
}
pub async fn set_mounts(&self, mount_dirs: Vec<storage::MountDir>) {
self.config.write().await.set_mounts(mount_dirs);
pub async fn set_mounts(&self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
self.config.write().await.set_mounts(mount_dirs)
// TODO persistence
}
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn can_read_config() {
let config_path = PathBuf::from_iter(["test-data", "config.toml"]);
let manager = Manager::new(&config_path, auth::Secret([0; 32]))
.await
.unwrap();
let config: storage::Config = manager.config.read().await.clone().into();
assert_eq!(config.reindex_every_n_seconds, None);
assert_eq!(
config.album_art_pattern,
Some(r#"^Folder\.(png|jpg|jpeg)$"#.to_owned())
);
assert_eq!(
config.mount_dirs,
vec![storage::MountDir {
source: PathBuf::from("test-data/small-collection"),
name: "root".to_owned(),
}]
);
assert_eq!(config.users[0].name, "test_user");
assert_eq!(config.users[0].admin, Some(true));
assert_eq!(
config.users[0].initial_password,
Some("very_secret_password".to_owned())
);
assert!(config.users[0].hashed_password.is_some());
}
}

View file

@ -1,4 +1,5 @@
use std::{
collections::HashMap,
ops::Deref,
path::{Path, PathBuf},
};
@ -38,17 +39,19 @@ impl From<MountDir> for storage::MountDir {
}
impl Config {
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) {
self.mount_dirs = mount_dirs
.into_iter()
.filter_map(|m| m.try_into().ok())
.collect();
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
let mut new_mount_dirs = HashMap::new();
for mount_dir in mount_dirs {
let mount_dir = <storage::MountDir as TryInto<MountDir>>::try_into(mount_dir)?;
new_mount_dirs.insert(mount_dir.name.clone(), mount_dir);
}
self.mount_dirs = new_mount_dirs;
Ok(())
}
pub fn resolve_virtual_path<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf, Error> {
for mount in &self.mount_dirs {
let mount_path = Path::new(&mount.name);
if let Ok(p) = virtual_path.as_ref().strip_prefix(mount_path) {
for (name, mount) in &self.mount_dirs {
if let Ok(p) = virtual_path.as_ref().strip_prefix(name) {
return if p.components().count() == 0 {
Ok(mount.source.clone())
} else {

View file

@ -161,7 +161,7 @@ async fn put_mount_dirs(
) -> Result<(), APIError> {
let new_mount_dirs: Vec<config::storage::MountDir> =
new_mount_dirs.iter().cloned().map(|m| m.into()).collect();
config_manager.set_mounts(new_mount_dirs).await;
config_manager.set_mounts(new_mount_dirs).await?;
Ok(())
}

View file

@ -1,4 +1,3 @@
[settings]
album_art_pattern = '^Folder\.(png|jpg|jpeg)$'
[[mount_dirs]]
@ -7,5 +6,5 @@ source = 'test-data/small-collection'
[[users]]
name = 'test_user'
password = 'very_secret_password'
initial_password = 'very_secret_password'
admin = true